12214ed89SReid Kleckner //===- MCCodeView.h - Machine Code CodeView support -------------*- C++ -*-===//
22214ed89SReid Kleckner //
32214ed89SReid Kleckner //                     The LLVM Compiler Infrastructure
42214ed89SReid Kleckner //
52214ed89SReid Kleckner // This file is distributed under the University of Illinois Open Source
62214ed89SReid Kleckner // License. See LICENSE.TXT for details.
72214ed89SReid Kleckner //
82214ed89SReid Kleckner //===----------------------------------------------------------------------===//
92214ed89SReid Kleckner //
102214ed89SReid Kleckner // Holds state from .cv_file and .cv_loc directives for later emission.
112214ed89SReid Kleckner //
122214ed89SReid Kleckner //===----------------------------------------------------------------------===//
132214ed89SReid Kleckner 
142214ed89SReid Kleckner #include "llvm/MC/MCCodeView.h"
152214ed89SReid Kleckner #include "llvm/ADT/STLExtras.h"
162214ed89SReid Kleckner #include "llvm/DebugInfo/CodeView/CodeView.h"
172214ed89SReid Kleckner #include "llvm/DebugInfo/CodeView/Line.h"
18*6fcbd7e9SDavid Majnemer #include "llvm/DebugInfo/CodeView/SymbolRecord.h"
192214ed89SReid Kleckner #include "llvm/MC/MCContext.h"
202214ed89SReid Kleckner #include "llvm/MC/MCObjectStreamer.h"
212214ed89SReid Kleckner #include "llvm/Support/COFF.h"
222214ed89SReid Kleckner 
232214ed89SReid Kleckner using namespace llvm;
242214ed89SReid Kleckner using namespace llvm::codeview;
252214ed89SReid Kleckner 
262214ed89SReid Kleckner CodeViewContext::CodeViewContext() {}
272214ed89SReid Kleckner 
282214ed89SReid Kleckner CodeViewContext::~CodeViewContext() {
292214ed89SReid Kleckner   // If someone inserted strings into the string table but never actually
302214ed89SReid Kleckner   // emitted them somewhere, clean up the fragment.
312214ed89SReid Kleckner   if (!InsertedStrTabFragment)
322214ed89SReid Kleckner     delete StrTabFragment;
332214ed89SReid Kleckner }
342214ed89SReid Kleckner 
352214ed89SReid Kleckner /// This is a valid number for use with .cv_loc if we've already seen a .cv_file
362214ed89SReid Kleckner /// for it.
372214ed89SReid Kleckner bool CodeViewContext::isValidFileNumber(unsigned FileNumber) const {
382214ed89SReid Kleckner   unsigned Idx = FileNumber - 1;
392214ed89SReid Kleckner   if (Idx < Filenames.size())
402214ed89SReid Kleckner     return !Filenames[Idx].empty();
412214ed89SReid Kleckner   return false;
422214ed89SReid Kleckner }
432214ed89SReid Kleckner 
442214ed89SReid Kleckner bool CodeViewContext::addFile(unsigned FileNumber, StringRef Filename) {
452214ed89SReid Kleckner   assert(FileNumber > 0);
462214ed89SReid Kleckner   Filename = addToStringTable(Filename);
472214ed89SReid Kleckner   unsigned Idx = FileNumber - 1;
482214ed89SReid Kleckner   if (Idx >= Filenames.size())
492214ed89SReid Kleckner     Filenames.resize(Idx + 1);
502214ed89SReid Kleckner 
512214ed89SReid Kleckner   if (Filename.empty())
522214ed89SReid Kleckner     Filename = "<stdin>";
532214ed89SReid Kleckner 
542214ed89SReid Kleckner   if (!Filenames[Idx].empty())
552214ed89SReid Kleckner     return false;
562214ed89SReid Kleckner 
572214ed89SReid Kleckner   // FIXME: We should store the string table offset of the filename, rather than
582214ed89SReid Kleckner   // the filename itself for efficiency.
592214ed89SReid Kleckner   Filename = addToStringTable(Filename);
602214ed89SReid Kleckner 
612214ed89SReid Kleckner   Filenames[Idx] = Filename;
622214ed89SReid Kleckner   return true;
632214ed89SReid Kleckner }
642214ed89SReid Kleckner 
652214ed89SReid Kleckner MCDataFragment *CodeViewContext::getStringTableFragment() {
662214ed89SReid Kleckner   if (!StrTabFragment) {
672214ed89SReid Kleckner     StrTabFragment = new MCDataFragment();
682214ed89SReid Kleckner     // Start a new string table out with a null byte.
692214ed89SReid Kleckner     StrTabFragment->getContents().push_back('\0');
702214ed89SReid Kleckner   }
712214ed89SReid Kleckner   return StrTabFragment;
722214ed89SReid Kleckner }
732214ed89SReid Kleckner 
742214ed89SReid Kleckner StringRef CodeViewContext::addToStringTable(StringRef S) {
752214ed89SReid Kleckner   SmallVectorImpl<char> &Contents = getStringTableFragment()->getContents();
762214ed89SReid Kleckner   auto Insertion =
772214ed89SReid Kleckner       StringTable.insert(std::make_pair(S, unsigned(Contents.size())));
782214ed89SReid Kleckner   // Return the string from the table, since it is stable.
792214ed89SReid Kleckner   S = Insertion.first->first();
802214ed89SReid Kleckner   if (Insertion.second) {
812214ed89SReid Kleckner     // The string map key is always null terminated.
822214ed89SReid Kleckner     Contents.append(S.begin(), S.end() + 1);
832214ed89SReid Kleckner   }
842214ed89SReid Kleckner   return S;
852214ed89SReid Kleckner }
862214ed89SReid Kleckner 
872214ed89SReid Kleckner unsigned CodeViewContext::getStringTableOffset(StringRef S) {
882214ed89SReid Kleckner   // A string table offset of zero is always the empty string.
892214ed89SReid Kleckner   if (S.empty())
902214ed89SReid Kleckner     return 0;
912214ed89SReid Kleckner   auto I = StringTable.find(S);
922214ed89SReid Kleckner   assert(I != StringTable.end());
932214ed89SReid Kleckner   return I->second;
942214ed89SReid Kleckner }
952214ed89SReid Kleckner 
962214ed89SReid Kleckner void CodeViewContext::emitStringTable(MCObjectStreamer &OS) {
972214ed89SReid Kleckner   MCContext &Ctx = OS.getContext();
98a0b44ef6SDavid Blaikie   MCSymbol *StringBegin = Ctx.createTempSymbol("strtab_begin", false),
99a0b44ef6SDavid Blaikie            *StringEnd = Ctx.createTempSymbol("strtab_end", false);
1002214ed89SReid Kleckner 
1012214ed89SReid Kleckner   OS.EmitIntValue(unsigned(ModuleSubstreamKind::StringTable), 4);
1022214ed89SReid Kleckner   OS.emitAbsoluteSymbolDiff(StringEnd, StringBegin, 4);
1032214ed89SReid Kleckner   OS.EmitLabel(StringBegin);
1042214ed89SReid Kleckner 
1052214ed89SReid Kleckner   // Put the string table data fragment here, if we haven't already put it
1062214ed89SReid Kleckner   // somewhere else. If somebody wants two string tables in their .s file, one
1072214ed89SReid Kleckner   // will just be empty.
1082214ed89SReid Kleckner   if (!InsertedStrTabFragment) {
1092214ed89SReid Kleckner     OS.insert(getStringTableFragment());
1102214ed89SReid Kleckner     InsertedStrTabFragment = true;
1112214ed89SReid Kleckner   }
1122214ed89SReid Kleckner 
1132214ed89SReid Kleckner   OS.EmitValueToAlignment(4, 0);
1142214ed89SReid Kleckner 
1152214ed89SReid Kleckner   OS.EmitLabel(StringEnd);
1162214ed89SReid Kleckner }
1172214ed89SReid Kleckner 
1182214ed89SReid Kleckner void CodeViewContext::emitFileChecksums(MCObjectStreamer &OS) {
1192214ed89SReid Kleckner   MCContext &Ctx = OS.getContext();
120a0b44ef6SDavid Blaikie   MCSymbol *FileBegin = Ctx.createTempSymbol("filechecksums_begin", false),
121a0b44ef6SDavid Blaikie            *FileEnd = Ctx.createTempSymbol("filechecksums_end", false);
1222214ed89SReid Kleckner 
1232214ed89SReid Kleckner   OS.EmitIntValue(unsigned(ModuleSubstreamKind::FileChecksums), 4);
1242214ed89SReid Kleckner   OS.emitAbsoluteSymbolDiff(FileEnd, FileBegin, 4);
1252214ed89SReid Kleckner   OS.EmitLabel(FileBegin);
1262214ed89SReid Kleckner 
1272214ed89SReid Kleckner   // Emit an array of FileChecksum entries. We index into this table using the
1282214ed89SReid Kleckner   // user-provided file number. Each entry is currently 8 bytes, as we don't
1292214ed89SReid Kleckner   // emit checksums.
1302214ed89SReid Kleckner   for (StringRef Filename : Filenames) {
1312214ed89SReid Kleckner     OS.EmitIntValue(getStringTableOffset(Filename), 4);
1322214ed89SReid Kleckner     // Zero the next two fields and align back to 4 bytes. This indicates that
1332214ed89SReid Kleckner     // no checksum is present.
1342214ed89SReid Kleckner     OS.EmitIntValue(0, 4);
1352214ed89SReid Kleckner   }
1362214ed89SReid Kleckner 
1372214ed89SReid Kleckner   OS.EmitLabel(FileEnd);
1382214ed89SReid Kleckner }
1392214ed89SReid Kleckner 
1402214ed89SReid Kleckner void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS,
1412214ed89SReid Kleckner                                                unsigned FuncId,
1422214ed89SReid Kleckner                                                const MCSymbol *FuncBegin,
1432214ed89SReid Kleckner                                                const MCSymbol *FuncEnd) {
1442214ed89SReid Kleckner   MCContext &Ctx = OS.getContext();
145a0b44ef6SDavid Blaikie   MCSymbol *LineBegin = Ctx.createTempSymbol("linetable_begin", false),
146a0b44ef6SDavid Blaikie            *LineEnd = Ctx.createTempSymbol("linetable_end", false);
1472214ed89SReid Kleckner 
1482214ed89SReid Kleckner   OS.EmitIntValue(unsigned(ModuleSubstreamKind::Lines), 4);
1492214ed89SReid Kleckner   OS.emitAbsoluteSymbolDiff(LineEnd, LineBegin, 4);
1502214ed89SReid Kleckner   OS.EmitLabel(LineBegin);
1512214ed89SReid Kleckner   OS.EmitCOFFSecRel32(FuncBegin);
1522214ed89SReid Kleckner   OS.EmitCOFFSectionIndex(FuncBegin);
1532214ed89SReid Kleckner 
1542214ed89SReid Kleckner   // Actual line info.
155*6fcbd7e9SDavid Majnemer   std::vector<MCCVLineEntry> Locs = getFunctionLineEntries(FuncId);
1562214ed89SReid Kleckner   bool HaveColumns = any_of(Locs, [](const MCCVLineEntry &LineEntry) {
1572214ed89SReid Kleckner     return LineEntry.getColumn() != 0;
1582214ed89SReid Kleckner   });
159*6fcbd7e9SDavid Majnemer   OS.EmitIntValue(HaveColumns ? int(LineFlags::HaveColumns) : 0, 2);
1602214ed89SReid Kleckner   OS.emitAbsoluteSymbolDiff(FuncEnd, FuncBegin, 4);
1612214ed89SReid Kleckner 
1622214ed89SReid Kleckner   for (auto I = Locs.begin(), E = Locs.end(); I != E;) {
1632214ed89SReid Kleckner     // Emit a file segment for the run of locations that share a file id.
1642214ed89SReid Kleckner     unsigned CurFileNum = I->getFileNum();
1652214ed89SReid Kleckner     auto FileSegEnd =
1662214ed89SReid Kleckner         std::find_if(I, E, [CurFileNum](const MCCVLineEntry &Loc) {
1672214ed89SReid Kleckner           return Loc.getFileNum() != CurFileNum;
1682214ed89SReid Kleckner         });
1692214ed89SReid Kleckner     unsigned EntryCount = FileSegEnd - I;
1702214ed89SReid Kleckner     OS.AddComment("Segment for file '" + Twine(Filenames[CurFileNum - 1]) +
1712214ed89SReid Kleckner                   "' begins");
1722214ed89SReid Kleckner     OS.EmitIntValue(8 * (CurFileNum - 1), 4);
1732214ed89SReid Kleckner     OS.EmitIntValue(EntryCount, 4);
1742214ed89SReid Kleckner     uint32_t SegmentSize = 12;
1752214ed89SReid Kleckner     SegmentSize += 8 * EntryCount;
1762214ed89SReid Kleckner     if (HaveColumns)
1772214ed89SReid Kleckner       SegmentSize += 4 * EntryCount;
1782214ed89SReid Kleckner     OS.EmitIntValue(SegmentSize, 4);
1792214ed89SReid Kleckner 
1802214ed89SReid Kleckner     for (auto J = I; J != FileSegEnd; ++J) {
1812214ed89SReid Kleckner       OS.emitAbsoluteSymbolDiff(J->getLabel(), FuncBegin, 4);
1822214ed89SReid Kleckner       unsigned LineData = J->getLine();
1832214ed89SReid Kleckner       if (J->isStmt())
184*6fcbd7e9SDavid Majnemer         LineData |= LineInfo::StatementFlag;
1852214ed89SReid Kleckner       OS.EmitIntValue(LineData, 4);
1862214ed89SReid Kleckner     }
1872214ed89SReid Kleckner     if (HaveColumns) {
1882214ed89SReid Kleckner       for (auto J = I; J != FileSegEnd; ++J) {
1892214ed89SReid Kleckner         OS.EmitIntValue(J->getColumn(), 2);
1902214ed89SReid Kleckner         OS.EmitIntValue(0, 2);
1912214ed89SReid Kleckner       }
1922214ed89SReid Kleckner     }
1932214ed89SReid Kleckner     I = FileSegEnd;
1942214ed89SReid Kleckner   }
1952214ed89SReid Kleckner   OS.EmitLabel(LineEnd);
1962214ed89SReid Kleckner }
1972214ed89SReid Kleckner 
198*6fcbd7e9SDavid Majnemer static bool compressAnnotation(uint32_t Data, SmallVectorImpl<char> &Buffer) {
199*6fcbd7e9SDavid Majnemer   if (isUInt<7>(Data)) {
200*6fcbd7e9SDavid Majnemer     Buffer.push_back(Data);
201*6fcbd7e9SDavid Majnemer     return true;
202*6fcbd7e9SDavid Majnemer   }
203*6fcbd7e9SDavid Majnemer 
204*6fcbd7e9SDavid Majnemer   if (isUInt<14>(Data)) {
205*6fcbd7e9SDavid Majnemer     Buffer.push_back((Data >> 8) | 0x80);
206*6fcbd7e9SDavid Majnemer     Buffer.push_back(Data & 0xff);
207*6fcbd7e9SDavid Majnemer     return true;
208*6fcbd7e9SDavid Majnemer   }
209*6fcbd7e9SDavid Majnemer 
210*6fcbd7e9SDavid Majnemer   if (isUInt<29>(Data)) {
211*6fcbd7e9SDavid Majnemer     Buffer.push_back((Data >> 24) | 0xC0);
212*6fcbd7e9SDavid Majnemer     Buffer.push_back((Data >> 16) & 0xff);
213*6fcbd7e9SDavid Majnemer     Buffer.push_back((Data >> 8) & 0xff);
214*6fcbd7e9SDavid Majnemer     Buffer.push_back(Data & 0xff);
215*6fcbd7e9SDavid Majnemer     return true;
216*6fcbd7e9SDavid Majnemer   }
217*6fcbd7e9SDavid Majnemer 
218*6fcbd7e9SDavid Majnemer   return false;
219*6fcbd7e9SDavid Majnemer }
220*6fcbd7e9SDavid Majnemer 
221*6fcbd7e9SDavid Majnemer static uint32_t encodeSignedNumber(uint32_t Data) {
222*6fcbd7e9SDavid Majnemer   if (Data >> 31)
223*6fcbd7e9SDavid Majnemer     return ((-Data) << 1) | 1;
224*6fcbd7e9SDavid Majnemer   return Data << 1;
225*6fcbd7e9SDavid Majnemer }
226*6fcbd7e9SDavid Majnemer 
227*6fcbd7e9SDavid Majnemer void CodeViewContext::emitInlineLineTableForFunction(
228*6fcbd7e9SDavid Majnemer     MCObjectStreamer &OS, unsigned PrimaryFunctionId, unsigned SourceFileId,
229*6fcbd7e9SDavid Majnemer     unsigned SourceLineNum, ArrayRef<unsigned> SecondaryFunctionIds) {
230*6fcbd7e9SDavid Majnemer   std::vector<MCCVLineEntry> Locs = getFunctionLineEntries(PrimaryFunctionId);
231*6fcbd7e9SDavid Majnemer   std::vector<std::pair<BinaryAnnotationsOpCode, uint32_t>> Annotations;
232*6fcbd7e9SDavid Majnemer 
233*6fcbd7e9SDavid Majnemer   const MCCVLineEntry *LastLoc = nullptr;
234*6fcbd7e9SDavid Majnemer   unsigned LastFileId = SourceFileId;
235*6fcbd7e9SDavid Majnemer   unsigned LastLineNum = SourceLineNum;
236*6fcbd7e9SDavid Majnemer 
237*6fcbd7e9SDavid Majnemer   for (const MCCVLineEntry &Loc : Locs) {
238*6fcbd7e9SDavid Majnemer     if (!LastLoc) {
239*6fcbd7e9SDavid Majnemer       // TODO ChangeCodeOffset
240*6fcbd7e9SDavid Majnemer       // TODO ChangeCodeLength
241*6fcbd7e9SDavid Majnemer     }
242*6fcbd7e9SDavid Majnemer 
243*6fcbd7e9SDavid Majnemer     if (Loc.getFileNum() != LastFileId)
244*6fcbd7e9SDavid Majnemer       Annotations.push_back({ChangeFile, Loc.getFileNum()});
245*6fcbd7e9SDavid Majnemer 
246*6fcbd7e9SDavid Majnemer     if (Loc.getLine() != LastLineNum)
247*6fcbd7e9SDavid Majnemer       Annotations.push_back(
248*6fcbd7e9SDavid Majnemer           {ChangeLineOffset, encodeSignedNumber(Loc.getLine() - LastLineNum)});
249*6fcbd7e9SDavid Majnemer 
250*6fcbd7e9SDavid Majnemer     LastLoc = &Loc;
251*6fcbd7e9SDavid Majnemer     LastFileId = Loc.getFileNum();
252*6fcbd7e9SDavid Majnemer     LastLineNum = Loc.getLine();
253*6fcbd7e9SDavid Majnemer   }
254*6fcbd7e9SDavid Majnemer 
255*6fcbd7e9SDavid Majnemer   SmallString<32> Buffer;
256*6fcbd7e9SDavid Majnemer   for (auto Annotation : Annotations) {
257*6fcbd7e9SDavid Majnemer     BinaryAnnotationsOpCode Opcode = Annotation.first;
258*6fcbd7e9SDavid Majnemer     uint32_t Operand = Annotation.second;
259*6fcbd7e9SDavid Majnemer     compressAnnotation(Opcode, Buffer);
260*6fcbd7e9SDavid Majnemer     compressAnnotation(Operand, Buffer);
261*6fcbd7e9SDavid Majnemer   }
262*6fcbd7e9SDavid Majnemer   OS.EmitBytes(Buffer);
263*6fcbd7e9SDavid Majnemer }
264*6fcbd7e9SDavid Majnemer 
2652214ed89SReid Kleckner //
2662214ed89SReid Kleckner // This is called when an instruction is assembled into the specified section
2672214ed89SReid Kleckner // and if there is information from the last .cv_loc directive that has yet to have
2682214ed89SReid Kleckner // a line entry made for it is made.
2692214ed89SReid Kleckner //
2702214ed89SReid Kleckner void MCCVLineEntry::Make(MCObjectStreamer *MCOS) {
2712214ed89SReid Kleckner   if (!MCOS->getContext().getCVLocSeen())
2722214ed89SReid Kleckner     return;
2732214ed89SReid Kleckner 
2742214ed89SReid Kleckner   // Create a symbol at in the current section for use in the line entry.
2752214ed89SReid Kleckner   MCSymbol *LineSym = MCOS->getContext().createTempSymbol();
2762214ed89SReid Kleckner   // Set the value of the symbol to use for the MCCVLineEntry.
2772214ed89SReid Kleckner   MCOS->EmitLabel(LineSym);
2782214ed89SReid Kleckner 
2792214ed89SReid Kleckner   // Get the current .loc info saved in the context.
2802214ed89SReid Kleckner   const MCCVLoc &CVLoc = MCOS->getContext().getCurrentCVLoc();
2812214ed89SReid Kleckner 
2822214ed89SReid Kleckner   // Create a (local) line entry with the symbol and the current .loc info.
2832214ed89SReid Kleckner   MCCVLineEntry LineEntry(LineSym, CVLoc);
2842214ed89SReid Kleckner 
2852214ed89SReid Kleckner   // clear CVLocSeen saying the current .loc info is now used.
2862214ed89SReid Kleckner   MCOS->getContext().clearCVLocSeen();
2872214ed89SReid Kleckner 
2882214ed89SReid Kleckner   // Add the line entry to this section's entries.
2892214ed89SReid Kleckner   MCOS->getContext().getCVContext().addLineEntry(LineEntry);
2902214ed89SReid Kleckner }
291