[Debuginfo][COFF] Minimal serialization support for precompiled types records

This change adds support for the LF_PRECOMP and LF_ENDPRECOMP records required
to read/write Microsoft precompiled types .objs.
See https://en.wikipedia.org/wiki/Precompiled_header#Microsoft_Visual_C_and_C++

This also adds handling for the .debug$P section, which is actually a .debug$T
section in disguise, found only in precompiled .objs.

Differential Revision: https://reviews.llvm.org/D45283

llvm-svn: 329613
This commit is contained in:
Alexandre Ganea 2018-04-09 20:17:56 +00:00
parent 69a2e18b4a
commit d9e96741c4
21 changed files with 4384 additions and 4203 deletions

View File

@ -22,8 +22,8 @@
namespace llvm {
namespace codeview {
/// Distinguishes individual records in .debug$T section or PDB type stream. The
/// documentation and headers talk about this as the "leaf" type.
/// Distinguishes individual records in .debug$T or .debug$P section or PDB type
/// stream. The documentation and headers talk about this as the "leaf" type.
enum class TypeRecordKind : uint16_t {
#define TYPE_RECORD(lf_ename, value, name) name = value,
#include "CodeViewTypes.def"

View File

@ -87,6 +87,8 @@ TYPE_RECORD(LF_UDT_MOD_SRC_LINE, 0x1607, UdtModSourceLine)
TYPE_RECORD(LF_METHODLIST, 0x1206, MethodOverloadList)
TYPE_RECORD(LF_PRECOMP, 0x1509, Precomp)
TYPE_RECORD(LF_ENDPRECOMP, 0x0014, EndPrecomp)
// 16 bit type records.
CV_TYPE(LF_MODIFIER_16t, 0x0001)
@ -106,7 +108,6 @@ CV_TYPE(LF_NOTTRAN, 0x0010)
CV_TYPE(LF_DIMARRAY_16t, 0x0011)
CV_TYPE(LF_VFTPATH_16t, 0x0012)
CV_TYPE(LF_PRECOMP_16t, 0x0013)
CV_TYPE(LF_ENDPRECOMP, 0x0014)
CV_TYPE(LF_OEM_16t, 0x0015)
CV_TYPE(LF_TYPESERVER_ST, 0x0016)
@ -181,7 +182,6 @@ CV_TYPE(LF_MANAGED_ST, 0x140f)
CV_TYPE(LF_ST_MAX, 0x1500)
CV_TYPE(LF_TYPESERVER, 0x1501)
CV_TYPE(LF_DIMARRAY, 0x1508)
CV_TYPE(LF_PRECOMP, 0x1509)
CV_TYPE(LF_ALIAS, 0x150a)
CV_TYPE(LF_DEFARG, 0x150b)
CV_TYPE(LF_FRIENDFCN, 0x150c)

View File

@ -896,6 +896,33 @@ public:
TypeIndex ContinuationIndex;
};
// LF_PRECOMP
class PrecompRecord : public TypeRecord {
public:
PrecompRecord() = default;
explicit PrecompRecord(TypeRecordKind Kind) : TypeRecord(Kind) {}
uint32_t getStartTypeIndex() const { return StartTypeIndex; }
uint32_t getTypesCount() const { return TypesCount; }
uint32_t getSignature() const { return Signature; }
StringRef getPrecompFilePath() const { return PrecompFilePath; }
uint32_t StartTypeIndex;
uint32_t TypesCount;
uint32_t Signature;
StringRef PrecompFilePath;
};
// LF_ENDPRECOMP
class EndPrecompRecord : public TypeRecord {
public:
EndPrecompRecord() = default;
explicit EndPrecompRecord(TypeRecordKind Kind) : TypeRecord(Kind) {}
uint32_t getSignature() const { return Signature; }
uint32_t Signature;
};
} // end namespace codeview
} // end namespace llvm

View File

@ -67,6 +67,7 @@ struct Section {
yaml::BinaryRef SectionData;
std::vector<CodeViewYAML::YAMLDebugSubsection> DebugS;
std::vector<CodeViewYAML::LeafRecord> DebugT;
std::vector<CodeViewYAML::LeafRecord> DebugP;
Optional<CodeViewYAML::DebugHSection> DebugH;
std::vector<Relocation> Relocations;
StringRef Name;

View File

@ -47,7 +47,7 @@ struct DebugHSection {
std::vector<GlobalHash> Hashes;
};
DebugHSection fromDebugH(ArrayRef<uint8_t> DebugT);
DebugHSection fromDebugH(ArrayRef<uint8_t> DebugH);
ArrayRef<uint8_t> toDebugH(const DebugHSection &DebugH,
BumpPtrAllocator &Alloc);

View File

@ -51,8 +51,10 @@ struct LeafRecord {
static Expected<LeafRecord> fromCodeViewRecord(codeview::CVType Type);
};
std::vector<LeafRecord> fromDebugT(ArrayRef<uint8_t> DebugT);
ArrayRef<uint8_t> toDebugT(ArrayRef<LeafRecord>, BumpPtrAllocator &Alloc);
std::vector<LeafRecord> fromDebugT(ArrayRef<uint8_t> DebugTorP,
StringRef SectionName);
ArrayRef<uint8_t> toDebugT(ArrayRef<LeafRecord>, BumpPtrAllocator &Alloc,
StringRef SectionName);
} // end namespace CodeViewYAML

View File

@ -524,7 +524,7 @@ void CodeViewDebug::emitTypeInformation() {
if (TypeTable.empty())
return;
// Start the .debug$T section with 0x4.
// Start the .debug$T or .debug$P section with 0x4.
OS.SwitchSection(Asm->getObjFileLowering().getCOFFDebugTypesSection());
emitCodeViewMagicVersion();

View File

@ -239,7 +239,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
}
/// Emit the magic version number at the start of a CodeView type or symbol
/// section. Appears at the front of every .debug$S or .debug$T section.
/// section. Appears at the front of every .debug$S or .debug$T or .debug$P
/// section.
void emitCodeViewMagicVersion();
void emitTypeInformation();

View File

@ -236,6 +236,16 @@ Error TypeNameComputer::visitKnownRecord(CVType &CVR, LabelRecord &R) {
return Error::success();
}
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
PrecompRecord &Precomp) {
return Error::success();
}
Error TypeNameComputer::visitKnownRecord(CVType &CVR,
EndPrecompRecord &EndPrecomp) {
return Error::success();
}
std::string llvm::codeview::computeTypeName(TypeCollection &Types,
TypeIndex Index) {
TypeNameComputer Computer(Types);

View File

@ -553,3 +553,18 @@ Error TypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &LR) {
W->printEnum("Mode", uint16_t(LR.Mode), makeArrayRef(LabelTypeEnum));
return Error::success();
}
Error TypeDumpVisitor::visitKnownRecord(CVType &CVR,
PrecompRecord &Precomp) {
W->printHex("StartIndex", Precomp.getStartTypeIndex());
W->printHex("Count", Precomp.getTypesCount());
W->printHex("Signature", Precomp.getSignature());
W->printString("PrecompFile", Precomp.getPrecompFilePath());
return Error::success();
}
Error TypeDumpVisitor::visitKnownRecord(CVType &CVR,
EndPrecompRecord &EndPrecomp) {
W->printHex("Signature", EndPrecomp.getSignature());
return Error::success();
}

View File

@ -480,3 +480,18 @@ Error TypeRecordMapping::visitKnownMember(CVMemberRecord &CVR,
return Error::success();
}
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
PrecompRecord &Precomp) {
error(IO.mapInteger(Precomp.StartTypeIndex));
error(IO.mapInteger(Precomp.TypesCount));
error(IO.mapInteger(Precomp.Signature));
error(IO.mapStringZ(Precomp.PrecompFilePath));
return Error::success();
}
Error TypeRecordMapping::visitKnownRecord(CVType &CVR,
EndPrecompRecord &EndPrecomp) {
error(IO.mapInteger(EndPrecomp.Signature));
return Error::success();
}

View File

@ -562,14 +562,16 @@ void MappingTraits<COFFYAML::Section>::mapping(IO &IO, COFFYAML::Section &Sec) {
IO.mapOptional("VirtualSize", Sec.Header.VirtualSize, 0U);
IO.mapOptional("Alignment", Sec.Alignment, 0U);
// If this is a .debug$S .debug$T, or .debug$H section parse the semantic
// representation of the symbols/types. If it is any other kind of section,
// just deal in raw bytes.
// If this is a .debug$S .debug$T .debug$P, or .debug$H section parse the
// semantic representation of the symbols/types. If it is any other kind
// of section, just deal in raw bytes.
IO.mapOptional("SectionData", Sec.SectionData);
if (Sec.Name == ".debug$S")
IO.mapOptional("Subsections", Sec.DebugS);
else if (Sec.Name == ".debug$T")
IO.mapOptional("Types", Sec.DebugT);
else if (Sec.Name == ".debug$P")
IO.mapOptional("PrecompTypes", Sec.DebugP);
else if (Sec.Name == ".debug$H")
IO.mapOptional("GlobalHashes", Sec.DebugH);

View File

@ -595,6 +595,17 @@ template <> void LeafRecordImpl<MethodOverloadListRecord>::map(IO &IO) {
IO.mapRequired("Methods", Record.Methods);
}
template <> void LeafRecordImpl<PrecompRecord>::map(IO &IO) {
IO.mapRequired("StartTypeIndex", Record.StartTypeIndex);
IO.mapRequired("TypesCount", Record.TypesCount);
IO.mapRequired("Signature", Record.Signature);
IO.mapRequired("PrecompFilePath", Record.PrecompFilePath);
}
template <> void LeafRecordImpl<EndPrecompRecord>::map(IO &IO) {
IO.mapRequired("Signature", Record.Signature);
}
template <> void MemberRecordImpl<OneMethodRecord>::map(IO &IO) {
MappingTraits<OneMethodRecord>::mapping(IO, Record);
}
@ -763,14 +774,16 @@ void MappingTraits<MemberRecord>::mapping(IO &IO, MemberRecord &Obj) {
}
std::vector<LeafRecord>
llvm::CodeViewYAML::fromDebugT(ArrayRef<uint8_t> DebugT) {
ExitOnError Err("Invalid .debug$T section!");
BinaryStreamReader Reader(DebugT, support::little);
llvm::CodeViewYAML::fromDebugT(ArrayRef<uint8_t> DebugTorP,
StringRef SectionName) {
ExitOnError Err("Invalid " + std::string(SectionName) + " section!");
BinaryStreamReader Reader(DebugTorP, support::little);
CVTypeArray Types;
uint32_t Magic;
Err(Reader.readInteger(Magic));
assert(Magic == COFF::DEBUG_SECTION_MAGIC && "Invalid .debug$T section!");
assert(Magic == COFF::DEBUG_SECTION_MAGIC &&
"Invalid .debug$T or .debug$P section!");
std::vector<LeafRecord> Result;
Err(Reader.readArray(Types, Reader.bytesRemaining()));
@ -782,7 +795,8 @@ llvm::CodeViewYAML::fromDebugT(ArrayRef<uint8_t> DebugT) {
}
ArrayRef<uint8_t> llvm::CodeViewYAML::toDebugT(ArrayRef<LeafRecord> Leafs,
BumpPtrAllocator &Alloc) {
BumpPtrAllocator &Alloc,
StringRef SectionName) {
AppendingTypeTableBuilder TS(Alloc);
uint32_t Size = sizeof(uint32_t);
for (const auto &Leaf : Leafs) {
@ -793,7 +807,8 @@ ArrayRef<uint8_t> llvm::CodeViewYAML::toDebugT(ArrayRef<LeafRecord> Leafs,
uint8_t *ResultBuffer = Alloc.Allocate<uint8_t>(Size);
MutableArrayRef<uint8_t> Output(ResultBuffer, Size);
BinaryStreamWriter Writer(Output, support::little);
ExitOnError Err("Error writing type record to .debug$T section");
ExitOnError Err("Error writing type record to " + std::string(SectionName) +
" section");
Err(Writer.writeInteger<uint32_t>(COFF::DEBUG_SECTION_MAGIC));
for (const auto &R : TS.records()) {
Err(Writer.writeBytes(R));

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,57 @@
RUN: rm -rf %t1/
RUN: mkdir %t1
RUN: obj2yaml %S/Inputs/precomp-a.obj > %t1/precomp-a.yaml
RUN: obj2yaml %S/Inputs/precomp.obj > %t1/precomp.yaml
RUN: yaml2obj %t1/precomp-a.yaml > %t1/a.obj
RUN: yaml2obj %t1/precomp.yaml > %t1/precomp.obj
RUN: llvm-readobj -codeview %t1/a.obj | FileCheck %s -check-prefix PRECOMP
RUN: llvm-readobj -codeview %t1/precomp.obj | FileCheck %s -check-prefix ENDPRECOMP
RUN: llvm-pdbutil dump -types %t1/a.obj | FileCheck %s -check-prefix PDB-PRECOMP
RUN: llvm-pdbutil dump -types %t1/precomp.obj | FileCheck %s -check-prefix PDB-ENDPRECOMP
ENDPRECOMP: CodeViewTypes [
ENDPRECOMP-NEXT: Section: .debug$P (3)
ENDPRECOMP: EndPrecomp (0x1407) {
ENDPRECOMP-NEXT: TypeLeafKind: LF_ENDPRECOMP (0x14)
ENDPRECOMP-NEXT: Signature: 0x1116980E
ENDPRECOMP-NEXT: }
PRECOMP: CodeViewTypes [
PRECOMP-NEXT: Section: .debug$T (3)
PRECOMP-NEXT: Magic: 0x4
PRECOMP-NEXT: Precomp (0x1000) {
PRECOMP-NEXT: TypeLeafKind: LF_PRECOMP (0x1509)
PRECOMP-NEXT: StartIndex: 0x1000
PRECOMP-NEXT: Count: 0x407
PRECOMP-NEXT: Signature: 0x1116980E
PDB-PRECOMP: Types (.debug$T)
PDB-PRECOMP-NEXT: ============================================================
PDB-PRECOMP-NEXT: Showing 0 records
PDB-PRECOMP-NEXT: 0x1000 | LF_PRECOMP [size = 60] start index = 0x1000, types count = 0x407, signature = 0x1116980E, precomp path = f:\svn\lld\test\coff\precomp\precomp.obj
PDB-ENDPRECOMP: Precompiled Types (.debug$P)
PDB-ENDPRECOMP-NEXT: ============================================================
PDB-ENDPRECOMP-NEXT: Showing 0 records
PDB-ENDPRECOMP: 0x1407 | LF_ENDPRECOMP [size = 8] signature = 0x1116980E
# // precomp.h
# #pragma once
# int Function(char A);
#
# // precomp.cpp
# // cl.exe precomp.cpp /Z7 /Ycprecomp.h /c
# #include "precomp.h"
#
# // a.cpp
# #include "precomp.h"
# int main(void) {
# Function('a');
# return 0;
# }
#
# // cl.exe a.cpp /Z7 /Yuprecomp.h /c
#
# // obj2yaml precomp.obj >precomp-precomp.yaml
# // obj2yaml a.obj >precomp-a.yaml

View File

@ -95,7 +95,8 @@ static inline bool isDebugSSection(object::SectionRef Section,
static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) {
BinaryStreamReader Reader;
if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader))
if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader) &&
!isCodeViewDebugSubsection(Section, ".debug$P", Reader))
return false;
cantFail(Reader.readArray(Types, Reader.bytesRemaining()));
return true;

View File

@ -467,6 +467,21 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
return Error::success();
}
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
PrecompRecord &Precomp) {
P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
" precomp path = {3}",
Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature,
Precomp.PrecompFilePath);
return Error::success();
}
Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
EndPrecompRecord &EP) {
P.format(" signature = {0:X+}", EP.Signature);
return Error::success();
}
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
NestedTypeRecord &Nested) {
P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);

View File

@ -170,7 +170,11 @@ void COFFDumper::dumpSections(unsigned NumSections) {
if (NewYAMLSection.Name == ".debug$S")
NewYAMLSection.DebugS = CodeViewYAML::fromDebugS(sectionData, SC);
else if (NewYAMLSection.Name == ".debug$T")
NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData);
NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData,
NewYAMLSection.Name);
else if (NewYAMLSection.Name == ".debug$P")
NewYAMLSection.DebugP = CodeViewYAML::fromDebugT(sectionData,
NewYAMLSection.Name);
else if (NewYAMLSection.Name == ".debug$H")
NewYAMLSection.DebugH = CodeViewYAML::fromDebugH(sectionData);

View File

@ -233,7 +233,10 @@ static bool layoutCOFF(COFFParser &CP) {
}
} else if (S.Name == ".debug$T") {
if (S.SectionData.binary_size() == 0)
S.SectionData = CodeViewYAML::toDebugT(S.DebugT, CP.Allocator);
S.SectionData = CodeViewYAML::toDebugT(S.DebugT, CP.Allocator, S.Name);
} else if (S.Name == ".debug$P") {
if (S.SectionData.binary_size() == 0)
S.SectionData = CodeViewYAML::toDebugT(S.DebugP, CP.Allocator, S.Name);
} else if (S.Name == ".debug$H") {
if (S.DebugH.hasValue() && S.SectionData.binary_size() == 0)
S.SectionData = CodeViewYAML::toDebugH(*S.DebugH, CP.Allocator);

View File

@ -580,3 +580,16 @@ TEST_F(TypeIndexIteratorTest, CallerSym) {
checkTypeReferences(2, TypeIndex(7), TypeIndex(8), TypeIndex(9));
}
TEST_F(TypeIndexIteratorTest, Precomp) {
PrecompRecord P(TypeRecordKind::Precomp);
P.StartTypeIndex = TypeIndex::FirstNonSimpleIndex;
P.TypesCount = 100;
P.Signature = 0x12345678;
P.PrecompFilePath = "C:/precomp.obj";
EndPrecompRecord EP(TypeRecordKind::EndPrecomp);
EP.Signature = P.Signature;
writeTypeRecords(P, EP);
checkTypeReferences(0);
}