1 | //===- MCCodeView.h - Machine Code CodeView support -------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Holds state from .cv_file and .cv_loc directives for later emission. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/MC/MCCodeView.h" |
14 | #include "llvm/ADT/STLExtras.h" |
15 | #include "llvm/ADT/StringExtras.h" |
16 | #include "llvm/DebugInfo/CodeView/CodeView.h" |
17 | #include "llvm/DebugInfo/CodeView/Line.h" |
18 | #include "llvm/DebugInfo/CodeView/SymbolRecord.h" |
19 | #include "llvm/MC/MCAsmLayout.h" |
20 | #include "llvm/MC/MCAssembler.h" |
21 | #include "llvm/MC/MCContext.h" |
22 | #include "llvm/MC/MCObjectStreamer.h" |
23 | #include "llvm/MC/MCValue.h" |
24 | #include "llvm/Support/EndianStream.h" |
25 | |
26 | using namespace llvm; |
27 | using namespace llvm::codeview; |
28 | |
29 | CodeViewContext::CodeViewContext() = default; |
30 | |
31 | CodeViewContext::~CodeViewContext() { |
32 | // If someone inserted strings into the string table but never actually |
33 | // emitted them somewhere, clean up the fragment. |
34 | if (!InsertedStrTabFragment) |
35 | delete StrTabFragment; |
36 | } |
37 | |
38 | /// This is a valid number for use with .cv_loc if we've already seen a .cv_file |
39 | /// for it. |
40 | bool CodeViewContext::isValidFileNumber(unsigned FileNumber) const { |
41 | unsigned Idx = FileNumber - 1; |
42 | if (Idx < Files.size()) |
43 | return Files[Idx].Assigned; |
44 | return false; |
45 | } |
46 | |
47 | bool CodeViewContext::addFile(MCStreamer &OS, unsigned FileNumber, |
48 | StringRef Filename, |
49 | ArrayRef<uint8_t> ChecksumBytes, |
50 | uint8_t ChecksumKind) { |
51 | assert(FileNumber > 0); |
52 | auto FilenameOffset = addToStringTable(S: Filename); |
53 | Filename = FilenameOffset.first; |
54 | unsigned Idx = FileNumber - 1; |
55 | if (Idx >= Files.size()) |
56 | Files.resize(N: Idx + 1); |
57 | |
58 | if (Filename.empty()) |
59 | Filename = "<stdin>" ; |
60 | |
61 | if (Files[Idx].Assigned) |
62 | return false; |
63 | |
64 | FilenameOffset = addToStringTable(S: Filename); |
65 | Filename = FilenameOffset.first; |
66 | unsigned Offset = FilenameOffset.second; |
67 | |
68 | auto ChecksumOffsetSymbol = |
69 | OS.getContext().createTempSymbol(Name: "checksum_offset" , AlwaysAddSuffix: false); |
70 | Files[Idx].StringTableOffset = Offset; |
71 | Files[Idx].ChecksumTableOffset = ChecksumOffsetSymbol; |
72 | Files[Idx].Assigned = true; |
73 | Files[Idx].Checksum = ChecksumBytes; |
74 | Files[Idx].ChecksumKind = ChecksumKind; |
75 | |
76 | return true; |
77 | } |
78 | |
79 | MCCVFunctionInfo *CodeViewContext::getCVFunctionInfo(unsigned FuncId) { |
80 | if (FuncId >= Functions.size()) |
81 | return nullptr; |
82 | if (Functions[FuncId].isUnallocatedFunctionInfo()) |
83 | return nullptr; |
84 | return &Functions[FuncId]; |
85 | } |
86 | |
87 | bool CodeViewContext::recordFunctionId(unsigned FuncId) { |
88 | if (FuncId >= Functions.size()) |
89 | Functions.resize(new_size: FuncId + 1); |
90 | |
91 | // Return false if this function info was already allocated. |
92 | if (!Functions[FuncId].isUnallocatedFunctionInfo()) |
93 | return false; |
94 | |
95 | // Mark this as an allocated normal function, and leave the rest alone. |
96 | Functions[FuncId].ParentFuncIdPlusOne = MCCVFunctionInfo::FunctionSentinel; |
97 | return true; |
98 | } |
99 | |
100 | bool CodeViewContext::recordInlinedCallSiteId(unsigned FuncId, unsigned IAFunc, |
101 | unsigned IAFile, unsigned IALine, |
102 | unsigned IACol) { |
103 | if (FuncId >= Functions.size()) |
104 | Functions.resize(new_size: FuncId + 1); |
105 | |
106 | // Return false if this function info was already allocated. |
107 | if (!Functions[FuncId].isUnallocatedFunctionInfo()) |
108 | return false; |
109 | |
110 | MCCVFunctionInfo::LineInfo InlinedAt; |
111 | InlinedAt.File = IAFile; |
112 | InlinedAt.Line = IALine; |
113 | InlinedAt.Col = IACol; |
114 | |
115 | // Mark this as an inlined call site and record call site line info. |
116 | MCCVFunctionInfo *Info = &Functions[FuncId]; |
117 | Info->ParentFuncIdPlusOne = IAFunc + 1; |
118 | Info->InlinedAt = InlinedAt; |
119 | |
120 | // Walk up the call chain adding this function id to the InlinedAtMap of all |
121 | // transitive callers until we hit a real function. |
122 | while (Info->isInlinedCallSite()) { |
123 | InlinedAt = Info->InlinedAt; |
124 | Info = getCVFunctionInfo(FuncId: Info->getParentFuncId()); |
125 | Info->InlinedAtMap[FuncId] = InlinedAt; |
126 | } |
127 | |
128 | return true; |
129 | } |
130 | |
131 | void CodeViewContext::recordCVLoc(MCContext &Ctx, const MCSymbol *Label, |
132 | unsigned FunctionId, unsigned FileNo, |
133 | unsigned Line, unsigned Column, |
134 | bool PrologueEnd, bool IsStmt) { |
135 | addLineEntry(LineEntry: MCCVLoc{ |
136 | Label, FunctionId, FileNo, Line, Column, PrologueEnd, IsStmt}); |
137 | } |
138 | |
139 | MCDataFragment *CodeViewContext::getStringTableFragment() { |
140 | if (!StrTabFragment) { |
141 | StrTabFragment = new MCDataFragment(); |
142 | // Start a new string table out with a null byte. |
143 | StrTabFragment->getContents().push_back(Elt: '\0'); |
144 | } |
145 | return StrTabFragment; |
146 | } |
147 | |
148 | std::pair<StringRef, unsigned> CodeViewContext::addToStringTable(StringRef S) { |
149 | SmallVectorImpl<char> &Contents = getStringTableFragment()->getContents(); |
150 | auto Insertion = |
151 | StringTable.insert(KV: std::make_pair(x&: S, y: unsigned(Contents.size()))); |
152 | // Return the string from the table, since it is stable. |
153 | std::pair<StringRef, unsigned> Ret = |
154 | std::make_pair(x: Insertion.first->first(), y&: Insertion.first->second); |
155 | if (Insertion.second) { |
156 | // The string map key is always null terminated. |
157 | Contents.append(in_start: Ret.first.begin(), in_end: Ret.first.end() + 1); |
158 | } |
159 | return Ret; |
160 | } |
161 | |
162 | unsigned CodeViewContext::getStringTableOffset(StringRef S) { |
163 | // A string table offset of zero is always the empty string. |
164 | if (S.empty()) |
165 | return 0; |
166 | auto I = StringTable.find(Key: S); |
167 | assert(I != StringTable.end()); |
168 | return I->second; |
169 | } |
170 | |
171 | void CodeViewContext::emitStringTable(MCObjectStreamer &OS) { |
172 | MCContext &Ctx = OS.getContext(); |
173 | MCSymbol *StringBegin = Ctx.createTempSymbol(Name: "strtab_begin" , AlwaysAddSuffix: false), |
174 | *StringEnd = Ctx.createTempSymbol(Name: "strtab_end" , AlwaysAddSuffix: false); |
175 | |
176 | OS.emitInt32(Value: uint32_t(DebugSubsectionKind::StringTable)); |
177 | OS.emitAbsoluteSymbolDiff(Hi: StringEnd, Lo: StringBegin, Size: 4); |
178 | OS.emitLabel(Symbol: StringBegin); |
179 | |
180 | // Put the string table data fragment here, if we haven't already put it |
181 | // somewhere else. If somebody wants two string tables in their .s file, one |
182 | // will just be empty. |
183 | if (!InsertedStrTabFragment) { |
184 | OS.insert(F: getStringTableFragment()); |
185 | InsertedStrTabFragment = true; |
186 | } |
187 | |
188 | OS.emitValueToAlignment(Alignment: Align(4), Value: 0); |
189 | |
190 | OS.emitLabel(Symbol: StringEnd); |
191 | } |
192 | |
193 | void CodeViewContext::emitFileChecksums(MCObjectStreamer &OS) { |
194 | // Do nothing if there are no file checksums. Microsoft's linker rejects empty |
195 | // CodeView substreams. |
196 | if (Files.empty()) |
197 | return; |
198 | |
199 | MCContext &Ctx = OS.getContext(); |
200 | MCSymbol *FileBegin = Ctx.createTempSymbol(Name: "filechecksums_begin" , AlwaysAddSuffix: false), |
201 | *FileEnd = Ctx.createTempSymbol(Name: "filechecksums_end" , AlwaysAddSuffix: false); |
202 | |
203 | OS.emitInt32(Value: uint32_t(DebugSubsectionKind::FileChecksums)); |
204 | OS.emitAbsoluteSymbolDiff(Hi: FileEnd, Lo: FileBegin, Size: 4); |
205 | OS.emitLabel(Symbol: FileBegin); |
206 | |
207 | unsigned CurrentOffset = 0; |
208 | |
209 | // Emit an array of FileChecksum entries. We index into this table using the |
210 | // user-provided file number. Each entry may be a variable number of bytes |
211 | // determined by the checksum kind and size. |
212 | for (auto File : Files) { |
213 | OS.emitAssignment(Symbol: File.ChecksumTableOffset, |
214 | Value: MCConstantExpr::create(Value: CurrentOffset, Ctx)); |
215 | CurrentOffset += 4; // String table offset. |
216 | if (!File.ChecksumKind) { |
217 | CurrentOffset += |
218 | 4; // One byte each for checksum size and kind, then align to 4 bytes. |
219 | } else { |
220 | CurrentOffset += 2; // One byte each for checksum size and kind. |
221 | CurrentOffset += File.Checksum.size(); |
222 | CurrentOffset = alignTo(Value: CurrentOffset, Align: 4); |
223 | } |
224 | |
225 | OS.emitInt32(Value: File.StringTableOffset); |
226 | |
227 | if (!File.ChecksumKind) { |
228 | // There is no checksum. Therefore zero the next two fields and align |
229 | // back to 4 bytes. |
230 | OS.emitInt32(Value: 0); |
231 | continue; |
232 | } |
233 | OS.emitInt8(Value: static_cast<uint8_t>(File.Checksum.size())); |
234 | OS.emitInt8(Value: File.ChecksumKind); |
235 | OS.emitBytes(Data: toStringRef(Input: File.Checksum)); |
236 | OS.emitValueToAlignment(Alignment: Align(4)); |
237 | } |
238 | |
239 | OS.emitLabel(Symbol: FileEnd); |
240 | |
241 | ChecksumOffsetsAssigned = true; |
242 | } |
243 | |
244 | // Output checksum table offset of the given file number. It is possible that |
245 | // not all files have been registered yet, and so the offset cannot be |
246 | // calculated. In this case a symbol representing the offset is emitted, and |
247 | // the value of this symbol will be fixed up at a later time. |
248 | void CodeViewContext::emitFileChecksumOffset(MCObjectStreamer &OS, |
249 | unsigned FileNo) { |
250 | unsigned Idx = FileNo - 1; |
251 | |
252 | if (Idx >= Files.size()) |
253 | Files.resize(N: Idx + 1); |
254 | |
255 | if (ChecksumOffsetsAssigned) { |
256 | OS.emitSymbolValue(Sym: Files[Idx].ChecksumTableOffset, Size: 4); |
257 | return; |
258 | } |
259 | |
260 | const MCSymbolRefExpr *SRE = |
261 | MCSymbolRefExpr::create(Symbol: Files[Idx].ChecksumTableOffset, Ctx&: OS.getContext()); |
262 | |
263 | OS.emitValueImpl(Value: SRE, Size: 4); |
264 | } |
265 | |
266 | void CodeViewContext::addLineEntry(const MCCVLoc &LineEntry) { |
267 | size_t Offset = MCCVLines.size(); |
268 | auto I = MCCVLineStartStop.insert( |
269 | x: {LineEntry.getFunctionId(), {Offset, Offset + 1}}); |
270 | if (!I.second) |
271 | I.first->second.second = Offset + 1; |
272 | MCCVLines.push_back(x: LineEntry); |
273 | } |
274 | |
275 | std::vector<MCCVLoc> |
276 | CodeViewContext::getFunctionLineEntries(unsigned FuncId) { |
277 | std::vector<MCCVLoc> FilteredLines; |
278 | size_t LocBegin; |
279 | size_t LocEnd; |
280 | std::tie(args&: LocBegin, args&: LocEnd) = getLineExtentIncludingInlinees(FuncId); |
281 | if (LocBegin >= LocEnd) { |
282 | return FilteredLines; |
283 | } |
284 | |
285 | MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(FuncId); |
286 | for (size_t Idx = LocBegin; Idx != LocEnd; ++Idx) { |
287 | unsigned LocationFuncId = MCCVLines[Idx].getFunctionId(); |
288 | if (LocationFuncId == FuncId) { |
289 | // This was a .cv_loc directly for FuncId, so record it. |
290 | FilteredLines.push_back(x: MCCVLines[Idx]); |
291 | } else { |
292 | // Check if the current location is inlined in this function. If it is, |
293 | // synthesize a statement .cv_loc at the original inlined call site. |
294 | auto I = SiteInfo->InlinedAtMap.find(Val: LocationFuncId); |
295 | if (I != SiteInfo->InlinedAtMap.end()) { |
296 | MCCVFunctionInfo::LineInfo &IA = I->second; |
297 | // Only add the location if it differs from the previous location. |
298 | // Large inlined calls will have many .cv_loc entries and we only need |
299 | // one line table entry in the parent function. |
300 | if (FilteredLines.empty() || |
301 | FilteredLines.back().getFileNum() != IA.File || |
302 | FilteredLines.back().getLine() != IA.Line || |
303 | FilteredLines.back().getColumn() != IA.Col) { |
304 | FilteredLines.push_back(x: MCCVLoc(MCCVLines[Idx].getLabel(), FuncId, |
305 | IA.File, IA.Line, IA.Col, false, |
306 | false)); |
307 | } |
308 | } |
309 | } |
310 | } |
311 | return FilteredLines; |
312 | } |
313 | |
314 | std::pair<size_t, size_t> CodeViewContext::getLineExtent(unsigned FuncId) { |
315 | auto I = MCCVLineStartStop.find(x: FuncId); |
316 | // Return an empty extent if there are no cv_locs for this function id. |
317 | if (I == MCCVLineStartStop.end()) |
318 | return {~0ULL, 0}; |
319 | return I->second; |
320 | } |
321 | |
322 | std::pair<size_t, size_t> |
323 | CodeViewContext::getLineExtentIncludingInlinees(unsigned FuncId) { |
324 | size_t LocBegin; |
325 | size_t LocEnd; |
326 | std::tie(args&: LocBegin, args&: LocEnd) = getLineExtent(FuncId); |
327 | |
328 | // Include all child inline call sites in our extent. |
329 | MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(FuncId); |
330 | if (SiteInfo) { |
331 | for (auto &KV : SiteInfo->InlinedAtMap) { |
332 | unsigned ChildId = KV.first; |
333 | auto Extent = getLineExtent(FuncId: ChildId); |
334 | LocBegin = std::min(a: LocBegin, b: Extent.first); |
335 | LocEnd = std::max(a: LocEnd, b: Extent.second); |
336 | } |
337 | } |
338 | |
339 | return {LocBegin, LocEnd}; |
340 | } |
341 | |
342 | ArrayRef<MCCVLoc> CodeViewContext::getLinesForExtent(size_t L, size_t R) { |
343 | if (R <= L) |
344 | return std::nullopt; |
345 | if (L >= MCCVLines.size()) |
346 | return std::nullopt; |
347 | return ArrayRef(&MCCVLines[L], R - L); |
348 | } |
349 | |
350 | void CodeViewContext::emitLineTableForFunction(MCObjectStreamer &OS, |
351 | unsigned FuncId, |
352 | const MCSymbol *FuncBegin, |
353 | const MCSymbol *FuncEnd) { |
354 | MCContext &Ctx = OS.getContext(); |
355 | MCSymbol *LineBegin = Ctx.createTempSymbol(Name: "linetable_begin" , AlwaysAddSuffix: false), |
356 | *LineEnd = Ctx.createTempSymbol(Name: "linetable_end" , AlwaysAddSuffix: false); |
357 | |
358 | OS.emitInt32(Value: uint32_t(DebugSubsectionKind::Lines)); |
359 | OS.emitAbsoluteSymbolDiff(Hi: LineEnd, Lo: LineBegin, Size: 4); |
360 | OS.emitLabel(Symbol: LineBegin); |
361 | OS.emitCOFFSecRel32(Symbol: FuncBegin, /*Offset=*/0); |
362 | OS.emitCOFFSectionIndex(Symbol: FuncBegin); |
363 | |
364 | // Actual line info. |
365 | std::vector<MCCVLoc> Locs = getFunctionLineEntries(FuncId); |
366 | bool HaveColumns = any_of(Range&: Locs, P: [](const MCCVLoc &LineEntry) { |
367 | return LineEntry.getColumn() != 0; |
368 | }); |
369 | OS.emitInt16(Value: HaveColumns ? int(LF_HaveColumns) : 0); |
370 | OS.emitAbsoluteSymbolDiff(Hi: FuncEnd, Lo: FuncBegin, Size: 4); |
371 | |
372 | for (auto I = Locs.begin(), E = Locs.end(); I != E;) { |
373 | // Emit a file segment for the run of locations that share a file id. |
374 | unsigned CurFileNum = I->getFileNum(); |
375 | auto FileSegEnd = |
376 | std::find_if(first: I, last: E, pred: [CurFileNum](const MCCVLoc &Loc) { |
377 | return Loc.getFileNum() != CurFileNum; |
378 | }); |
379 | unsigned EntryCount = FileSegEnd - I; |
380 | OS.AddComment( |
381 | T: "Segment for file '" + |
382 | Twine(getStringTableFragment() |
383 | ->getContents()[Files[CurFileNum - 1].StringTableOffset]) + |
384 | "' begins" ); |
385 | OS.emitCVFileChecksumOffsetDirective(FileNo: CurFileNum); |
386 | OS.emitInt32(Value: EntryCount); |
387 | uint32_t SegmentSize = 12; |
388 | SegmentSize += 8 * EntryCount; |
389 | if (HaveColumns) |
390 | SegmentSize += 4 * EntryCount; |
391 | OS.emitInt32(Value: SegmentSize); |
392 | |
393 | for (auto J = I; J != FileSegEnd; ++J) { |
394 | OS.emitAbsoluteSymbolDiff(Hi: J->getLabel(), Lo: FuncBegin, Size: 4); |
395 | unsigned LineData = J->getLine(); |
396 | if (J->isStmt()) |
397 | LineData |= LineInfo::StatementFlag; |
398 | OS.emitInt32(Value: LineData); |
399 | } |
400 | if (HaveColumns) { |
401 | for (auto J = I; J != FileSegEnd; ++J) { |
402 | OS.emitInt16(Value: J->getColumn()); |
403 | OS.emitInt16(Value: 0); |
404 | } |
405 | } |
406 | I = FileSegEnd; |
407 | } |
408 | OS.emitLabel(Symbol: LineEnd); |
409 | } |
410 | |
411 | static bool compressAnnotation(uint32_t Data, SmallVectorImpl<char> &Buffer) { |
412 | if (isUInt<7>(x: Data)) { |
413 | Buffer.push_back(Elt: Data); |
414 | return true; |
415 | } |
416 | |
417 | if (isUInt<14>(x: Data)) { |
418 | Buffer.push_back(Elt: (Data >> 8) | 0x80); |
419 | Buffer.push_back(Elt: Data & 0xff); |
420 | return true; |
421 | } |
422 | |
423 | if (isUInt<29>(x: Data)) { |
424 | Buffer.push_back(Elt: (Data >> 24) | 0xC0); |
425 | Buffer.push_back(Elt: (Data >> 16) & 0xff); |
426 | Buffer.push_back(Elt: (Data >> 8) & 0xff); |
427 | Buffer.push_back(Elt: Data & 0xff); |
428 | return true; |
429 | } |
430 | |
431 | return false; |
432 | } |
433 | |
434 | static bool compressAnnotation(BinaryAnnotationsOpCode Annotation, |
435 | SmallVectorImpl<char> &Buffer) { |
436 | return compressAnnotation(Data: static_cast<uint32_t>(Annotation), Buffer); |
437 | } |
438 | |
439 | static uint32_t encodeSignedNumber(uint32_t Data) { |
440 | if (Data >> 31) |
441 | return ((-Data) << 1) | 1; |
442 | return Data << 1; |
443 | } |
444 | |
445 | void CodeViewContext::emitInlineLineTableForFunction(MCObjectStreamer &OS, |
446 | unsigned PrimaryFunctionId, |
447 | unsigned SourceFileId, |
448 | unsigned SourceLineNum, |
449 | const MCSymbol *FnStartSym, |
450 | const MCSymbol *FnEndSym) { |
451 | // Create and insert a fragment into the current section that will be encoded |
452 | // later. |
453 | new MCCVInlineLineTableFragment(PrimaryFunctionId, SourceFileId, |
454 | SourceLineNum, FnStartSym, FnEndSym, |
455 | OS.getCurrentSectionOnly()); |
456 | } |
457 | |
458 | MCFragment *CodeViewContext::emitDefRange( |
459 | MCObjectStreamer &OS, |
460 | ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, |
461 | StringRef FixedSizePortion) { |
462 | // Create and insert a fragment into the current section that will be encoded |
463 | // later. |
464 | return new MCCVDefRangeFragment(Ranges, FixedSizePortion, |
465 | OS.getCurrentSectionOnly()); |
466 | } |
467 | |
468 | static unsigned computeLabelDiff(MCAsmLayout &Layout, const MCSymbol *Begin, |
469 | const MCSymbol *End) { |
470 | MCContext &Ctx = Layout.getAssembler().getContext(); |
471 | MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None; |
472 | const MCExpr *BeginRef = MCSymbolRefExpr::create(Symbol: Begin, Kind: Variant, Ctx), |
473 | *EndRef = MCSymbolRefExpr::create(Symbol: End, Kind: Variant, Ctx); |
474 | const MCExpr *AddrDelta = |
475 | MCBinaryExpr::create(Op: MCBinaryExpr::Sub, LHS: EndRef, RHS: BeginRef, Ctx); |
476 | int64_t Result; |
477 | bool Success = AddrDelta->evaluateKnownAbsolute(Res&: Result, Layout); |
478 | assert(Success && "failed to evaluate label difference as absolute" ); |
479 | (void)Success; |
480 | assert(Result >= 0 && "negative label difference requested" ); |
481 | assert(Result < UINT_MAX && "label difference greater than 2GB" ); |
482 | return unsigned(Result); |
483 | } |
484 | |
485 | void CodeViewContext::encodeInlineLineTable(MCAsmLayout &Layout, |
486 | MCCVInlineLineTableFragment &Frag) { |
487 | size_t LocBegin; |
488 | size_t LocEnd; |
489 | std::tie(args&: LocBegin, args&: LocEnd) = getLineExtentIncludingInlinees(FuncId: Frag.SiteFuncId); |
490 | |
491 | if (LocBegin >= LocEnd) |
492 | return; |
493 | ArrayRef<MCCVLoc> Locs = getLinesForExtent(L: LocBegin, R: LocEnd); |
494 | if (Locs.empty()) |
495 | return; |
496 | |
497 | // Check that the locations are all in the same section. |
498 | #ifndef NDEBUG |
499 | const MCSection *FirstSec = &Locs.front().getLabel()->getSection(); |
500 | for (const MCCVLoc &Loc : Locs) { |
501 | if (&Loc.getLabel()->getSection() != FirstSec) { |
502 | errs() << ".cv_loc " << Loc.getFunctionId() << ' ' << Loc.getFileNum() |
503 | << ' ' << Loc.getLine() << ' ' << Loc.getColumn() |
504 | << " is in the wrong section\n" ; |
505 | llvm_unreachable(".cv_loc crosses sections" ); |
506 | } |
507 | } |
508 | #endif |
509 | |
510 | // Make an artificial start location using the function start and the inlinee |
511 | // lines start location information. All deltas start relative to this |
512 | // location. |
513 | MCCVLoc StartLoc = Locs.front(); |
514 | StartLoc.setLabel(Frag.getFnStartSym()); |
515 | StartLoc.setFileNum(Frag.StartFileId); |
516 | StartLoc.setLine(Frag.StartLineNum); |
517 | bool HaveOpenRange = false; |
518 | |
519 | const MCSymbol *LastLabel = Frag.getFnStartSym(); |
520 | MCCVFunctionInfo::LineInfo LastSourceLoc, CurSourceLoc; |
521 | LastSourceLoc.File = Frag.StartFileId; |
522 | LastSourceLoc.Line = Frag.StartLineNum; |
523 | |
524 | MCCVFunctionInfo *SiteInfo = getCVFunctionInfo(FuncId: Frag.SiteFuncId); |
525 | |
526 | SmallVectorImpl<char> &Buffer = Frag.getContents(); |
527 | Buffer.clear(); // Clear old contents if we went through relaxation. |
528 | for (const MCCVLoc &Loc : Locs) { |
529 | // Exit early if our line table would produce an oversized InlineSiteSym |
530 | // record. Account for the ChangeCodeLength annotation emitted after the |
531 | // loop ends. |
532 | constexpr uint32_t InlineSiteSize = 12; |
533 | constexpr uint32_t AnnotationSize = 8; |
534 | size_t MaxBufferSize = MaxRecordLength - InlineSiteSize - AnnotationSize; |
535 | if (Buffer.size() >= MaxBufferSize) |
536 | break; |
537 | |
538 | if (Loc.getFunctionId() == Frag.SiteFuncId) { |
539 | CurSourceLoc.File = Loc.getFileNum(); |
540 | CurSourceLoc.Line = Loc.getLine(); |
541 | } else { |
542 | auto I = SiteInfo->InlinedAtMap.find(Val: Loc.getFunctionId()); |
543 | if (I != SiteInfo->InlinedAtMap.end()) { |
544 | // This .cv_loc is from a child inline call site. Use the source |
545 | // location of the inlined call site instead of the .cv_loc directive |
546 | // source location. |
547 | CurSourceLoc = I->second; |
548 | } else { |
549 | // We've hit a cv_loc not attributed to this inline call site. Use this |
550 | // label to end the PC range. |
551 | if (HaveOpenRange) { |
552 | unsigned Length = computeLabelDiff(Layout, Begin: LastLabel, End: Loc.getLabel()); |
553 | compressAnnotation(Annotation: BinaryAnnotationsOpCode::ChangeCodeLength, Buffer); |
554 | compressAnnotation(Data: Length, Buffer); |
555 | LastLabel = Loc.getLabel(); |
556 | } |
557 | HaveOpenRange = false; |
558 | continue; |
559 | } |
560 | } |
561 | |
562 | // Skip this .cv_loc if we have an open range and this isn't a meaningful |
563 | // source location update. The current table format does not support column |
564 | // info, so we can skip updates for those. |
565 | if (HaveOpenRange && CurSourceLoc.File == LastSourceLoc.File && |
566 | CurSourceLoc.Line == LastSourceLoc.Line) |
567 | continue; |
568 | |
569 | HaveOpenRange = true; |
570 | |
571 | if (CurSourceLoc.File != LastSourceLoc.File) { |
572 | unsigned FileOffset = static_cast<const MCConstantExpr *>( |
573 | Files[CurSourceLoc.File - 1] |
574 | .ChecksumTableOffset->getVariableValue()) |
575 | ->getValue(); |
576 | compressAnnotation(Annotation: BinaryAnnotationsOpCode::ChangeFile, Buffer); |
577 | compressAnnotation(Data: FileOffset, Buffer); |
578 | } |
579 | |
580 | int LineDelta = CurSourceLoc.Line - LastSourceLoc.Line; |
581 | unsigned EncodedLineDelta = encodeSignedNumber(Data: LineDelta); |
582 | unsigned CodeDelta = computeLabelDiff(Layout, Begin: LastLabel, End: Loc.getLabel()); |
583 | if (EncodedLineDelta < 0x8 && CodeDelta <= 0xf) { |
584 | // The ChangeCodeOffsetAndLineOffset combination opcode is used when the |
585 | // encoded line delta uses 3 or fewer set bits and the code offset fits |
586 | // in one nibble. |
587 | unsigned Operand = (EncodedLineDelta << 4) | CodeDelta; |
588 | compressAnnotation(Annotation: BinaryAnnotationsOpCode::ChangeCodeOffsetAndLineOffset, |
589 | Buffer); |
590 | compressAnnotation(Data: Operand, Buffer); |
591 | } else { |
592 | // Otherwise use the separate line and code deltas. |
593 | if (LineDelta != 0) { |
594 | compressAnnotation(Annotation: BinaryAnnotationsOpCode::ChangeLineOffset, Buffer); |
595 | compressAnnotation(Data: EncodedLineDelta, Buffer); |
596 | } |
597 | compressAnnotation(Annotation: BinaryAnnotationsOpCode::ChangeCodeOffset, Buffer); |
598 | compressAnnotation(Data: CodeDelta, Buffer); |
599 | } |
600 | |
601 | LastLabel = Loc.getLabel(); |
602 | LastSourceLoc = CurSourceLoc; |
603 | } |
604 | |
605 | assert(HaveOpenRange); |
606 | |
607 | unsigned EndSymLength = |
608 | computeLabelDiff(Layout, Begin: LastLabel, End: Frag.getFnEndSym()); |
609 | unsigned LocAfterLength = ~0U; |
610 | ArrayRef<MCCVLoc> LocAfter = getLinesForExtent(L: LocEnd, R: LocEnd + 1); |
611 | if (!LocAfter.empty()) { |
612 | // Only try to compute this difference if we're in the same section. |
613 | const MCCVLoc &Loc = LocAfter[0]; |
614 | if (&Loc.getLabel()->getSection() == &LastLabel->getSection()) |
615 | LocAfterLength = computeLabelDiff(Layout, Begin: LastLabel, End: Loc.getLabel()); |
616 | } |
617 | |
618 | compressAnnotation(Annotation: BinaryAnnotationsOpCode::ChangeCodeLength, Buffer); |
619 | compressAnnotation(Data: std::min(a: EndSymLength, b: LocAfterLength), Buffer); |
620 | } |
621 | |
622 | void CodeViewContext::encodeDefRange(MCAsmLayout &Layout, |
623 | MCCVDefRangeFragment &Frag) { |
624 | MCContext &Ctx = Layout.getAssembler().getContext(); |
625 | SmallVectorImpl<char> &Contents = Frag.getContents(); |
626 | Contents.clear(); |
627 | SmallVectorImpl<MCFixup> &Fixups = Frag.getFixups(); |
628 | Fixups.clear(); |
629 | raw_svector_ostream OS(Contents); |
630 | |
631 | // Compute all the sizes up front. |
632 | SmallVector<std::pair<unsigned, unsigned>, 4> GapAndRangeSizes; |
633 | const MCSymbol *LastLabel = nullptr; |
634 | for (std::pair<const MCSymbol *, const MCSymbol *> Range : Frag.getRanges()) { |
635 | unsigned GapSize = |
636 | LastLabel ? computeLabelDiff(Layout, Begin: LastLabel, End: Range.first) : 0; |
637 | unsigned RangeSize = computeLabelDiff(Layout, Begin: Range.first, End: Range.second); |
638 | GapAndRangeSizes.push_back(Elt: {GapSize, RangeSize}); |
639 | LastLabel = Range.second; |
640 | } |
641 | |
642 | // Write down each range where the variable is defined. |
643 | for (size_t I = 0, E = Frag.getRanges().size(); I != E;) { |
644 | // If the range size of multiple consecutive ranges is under the max, |
645 | // combine the ranges and emit some gaps. |
646 | const MCSymbol *RangeBegin = Frag.getRanges()[I].first; |
647 | unsigned RangeSize = GapAndRangeSizes[I].second; |
648 | size_t J = I + 1; |
649 | for (; J != E; ++J) { |
650 | unsigned GapAndRangeSize = GapAndRangeSizes[J].first + GapAndRangeSizes[J].second; |
651 | if (RangeSize + GapAndRangeSize > MaxDefRange) |
652 | break; |
653 | RangeSize += GapAndRangeSize; |
654 | } |
655 | unsigned NumGaps = J - I - 1; |
656 | |
657 | support::endian::Writer LEWriter(OS, llvm::endianness::little); |
658 | |
659 | unsigned Bias = 0; |
660 | // We must split the range into chunks of MaxDefRange, this is a fundamental |
661 | // limitation of the file format. |
662 | do { |
663 | uint16_t Chunk = std::min(a: (uint32_t)MaxDefRange, b: RangeSize); |
664 | |
665 | const MCSymbolRefExpr *SRE = MCSymbolRefExpr::create(Symbol: RangeBegin, Ctx); |
666 | const MCBinaryExpr *BE = |
667 | MCBinaryExpr::createAdd(LHS: SRE, RHS: MCConstantExpr::create(Value: Bias, Ctx), Ctx); |
668 | |
669 | // Each record begins with a 2-byte number indicating how large the record |
670 | // is. |
671 | StringRef FixedSizePortion = Frag.getFixedSizePortion(); |
672 | // Our record is a fixed sized prefix and a LocalVariableAddrRange that we |
673 | // are artificially constructing. |
674 | size_t RecordSize = FixedSizePortion.size() + |
675 | sizeof(LocalVariableAddrRange) + 4 * NumGaps; |
676 | // Write out the record size. |
677 | LEWriter.write<uint16_t>(Val: RecordSize); |
678 | // Write out the fixed size prefix. |
679 | OS << FixedSizePortion; |
680 | // Make space for a fixup that will eventually have a section relative |
681 | // relocation pointing at the offset where the variable becomes live. |
682 | Fixups.push_back(Elt: MCFixup::create(Offset: Contents.size(), Value: BE, Kind: FK_SecRel_4)); |
683 | LEWriter.write<uint32_t>(Val: 0); // Fixup for code start. |
684 | // Make space for a fixup that will record the section index for the code. |
685 | Fixups.push_back(Elt: MCFixup::create(Offset: Contents.size(), Value: BE, Kind: FK_SecRel_2)); |
686 | LEWriter.write<uint16_t>(Val: 0); // Fixup for section index. |
687 | // Write down the range's extent. |
688 | LEWriter.write<uint16_t>(Val: Chunk); |
689 | |
690 | // Move on to the next range. |
691 | Bias += Chunk; |
692 | RangeSize -= Chunk; |
693 | } while (RangeSize > 0); |
694 | |
695 | // Emit the gaps afterwards. |
696 | assert((NumGaps == 0 || Bias <= MaxDefRange) && |
697 | "large ranges should not have gaps" ); |
698 | unsigned GapStartOffset = GapAndRangeSizes[I].second; |
699 | for (++I; I != J; ++I) { |
700 | unsigned GapSize, RangeSize; |
701 | assert(I < GapAndRangeSizes.size()); |
702 | std::tie(args&: GapSize, args&: RangeSize) = GapAndRangeSizes[I]; |
703 | LEWriter.write<uint16_t>(Val: GapStartOffset); |
704 | LEWriter.write<uint16_t>(Val: GapSize); |
705 | GapStartOffset += GapSize + RangeSize; |
706 | } |
707 | } |
708 | } |
709 | |