1//===-- RecordSerialization.cpp -------------------------------------------===//
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// Utilities for serializing and deserializing CodeView records.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/DebugInfo/CodeView/RecordSerialization.h"
14#include "llvm/ADT/APInt.h"
15#include "llvm/ADT/APSInt.h"
16#include "llvm/DebugInfo/CodeView/CVRecord.h"
17#include "llvm/DebugInfo/CodeView/CodeViewError.h"
18#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
19#include "llvm/Support/BinaryByteStream.h"
20
21using namespace llvm;
22using namespace llvm::codeview;
23using namespace llvm::support;
24
25/// Reinterpret a byte array as an array of characters. Does not interpret as
26/// a C string, as StringRef has several helpers (split) that make that easy.
27StringRef llvm::codeview::getBytesAsCharacters(ArrayRef<uint8_t> LeafData) {
28 return StringRef(reinterpret_cast<const char *>(LeafData.data()),
29 LeafData.size());
30}
31
32StringRef llvm::codeview::getBytesAsCString(ArrayRef<uint8_t> LeafData) {
33 return getBytesAsCharacters(LeafData).split(Separator: '\0').first;
34}
35
36Error llvm::codeview::consume(BinaryStreamReader &Reader, APSInt &Num) {
37 // Used to avoid overload ambiguity on APInt constructor.
38 bool FalseVal = false;
39 uint16_t Short;
40 if (auto EC = Reader.readInteger(Dest&: Short))
41 return EC;
42
43 if (Short < LF_NUMERIC) {
44 Num = APSInt(APInt(/*numBits=*/16, Short, /*isSigned=*/false),
45 /*isUnsigned=*/true);
46 return Error::success();
47 }
48
49 switch (Short) {
50 case LF_CHAR: {
51 int8_t N;
52 if (auto EC = Reader.readInteger(Dest&: N))
53 return EC;
54 Num = APSInt(APInt(8, N, true), false);
55 return Error::success();
56 }
57 case LF_SHORT: {
58 int16_t N;
59 if (auto EC = Reader.readInteger(Dest&: N))
60 return EC;
61 Num = APSInt(APInt(16, N, true), false);
62 return Error::success();
63 }
64 case LF_USHORT: {
65 uint16_t N;
66 if (auto EC = Reader.readInteger(Dest&: N))
67 return EC;
68 Num = APSInt(APInt(16, N, false), true);
69 return Error::success();
70 }
71 case LF_LONG: {
72 int32_t N;
73 if (auto EC = Reader.readInteger(Dest&: N))
74 return EC;
75 Num = APSInt(APInt(32, N, true), false);
76 return Error::success();
77 }
78 case LF_ULONG: {
79 uint32_t N;
80 if (auto EC = Reader.readInteger(Dest&: N))
81 return EC;
82 Num = APSInt(APInt(32, N, FalseVal), true);
83 return Error::success();
84 }
85 case LF_QUADWORD: {
86 int64_t N;
87 if (auto EC = Reader.readInteger(Dest&: N))
88 return EC;
89 Num = APSInt(APInt(64, N, true), false);
90 return Error::success();
91 }
92 case LF_UQUADWORD: {
93 uint64_t N;
94 if (auto EC = Reader.readInteger(Dest&: N))
95 return EC;
96 Num = APSInt(APInt(64, N, false), true);
97 return Error::success();
98 }
99 }
100 return make_error<CodeViewError>(Args: cv_error_code::corrupt_record,
101 Args: "Buffer contains invalid APSInt type");
102}
103
104Error llvm::codeview::consume(StringRef &Data, APSInt &Num) {
105 ArrayRef<uint8_t> Bytes(Data.bytes_begin(), Data.bytes_end());
106 BinaryByteStream S(Bytes, llvm::endianness::little);
107 BinaryStreamReader SR(S);
108 auto EC = consume(Reader&: SR, Num);
109 Data = Data.take_back(N: SR.bytesRemaining());
110 return EC;
111}
112
113/// Decode a numeric leaf value that is known to be a uint64_t.
114Error llvm::codeview::consume_numeric(BinaryStreamReader &Reader,
115 uint64_t &Num) {
116 APSInt N;
117 if (auto EC = consume(Reader, Num&: N))
118 return EC;
119 if (N.isSigned() || !N.isIntN(N: 64))
120 return make_error<CodeViewError>(Args: cv_error_code::corrupt_record,
121 Args: "Data is not a numeric value!");
122 Num = N.getLimitedValue();
123 return Error::success();
124}
125
126Error llvm::codeview::consume(BinaryStreamReader &Reader, uint32_t &Item) {
127 return Reader.readInteger(Dest&: Item);
128}
129
130Error llvm::codeview::consume(StringRef &Data, uint32_t &Item) {
131 ArrayRef<uint8_t> Bytes(Data.bytes_begin(), Data.bytes_end());
132 BinaryByteStream S(Bytes, llvm::endianness::little);
133 BinaryStreamReader SR(S);
134 auto EC = consume(Reader&: SR, Item);
135 Data = Data.take_back(N: SR.bytesRemaining());
136 return EC;
137}
138
139Error llvm::codeview::consume(BinaryStreamReader &Reader, int32_t &Item) {
140 return Reader.readInteger(Dest&: Item);
141}
142
143Error llvm::codeview::consume(BinaryStreamReader &Reader, StringRef &Item) {
144 if (Reader.empty())
145 return make_error<CodeViewError>(Args: cv_error_code::corrupt_record,
146 Args: "Null terminated string buffer is empty!");
147
148 return Reader.readCString(Dest&: Item);
149}
150
151Expected<CVSymbol> llvm::codeview::readSymbolFromStream(BinaryStreamRef Stream,
152 uint32_t Offset) {
153 return readCVRecordFromStream<SymbolKind>(Stream, Offset);
154}
155