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('\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(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(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(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(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(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(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(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(N))
95      return EC;
96    Num = APSInt(APInt(64, N, false), true);
97    return Error::success();
98  }
99  }
100  return make_error<CodeViewError>(cv_error_code::corrupt_record,
101                                   "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::support::little);
107  BinaryStreamReader SR(S);
108  auto EC = consume(SR, Num);
109  Data = Data.take_back(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, N))
118    return EC;
119  if (N.isSigned() || !N.isIntN(64))
120    return make_error<CodeViewError>(cv_error_code::corrupt_record,
121                                     "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(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::support::little);
133  BinaryStreamReader SR(S);
134  auto EC = consume(SR, Item);
135  Data = Data.take_back(SR.bytesRemaining());
136  return EC;
137}
138
139Error llvm::codeview::consume(BinaryStreamReader &Reader, int32_t &Item) {
140  return Reader.readInteger(Item);
141}
142
143Error llvm::codeview::consume(BinaryStreamReader &Reader, StringRef &Item) {
144  if (Reader.empty())
145    return make_error<CodeViewError>(cv_error_code::corrupt_record,
146                                     "Null terminated string buffer is empty!");
147
148  return Reader.readCString(Item);
149}
150
151Expected<CVSymbol> llvm::codeview::readSymbolFromStream(BinaryStreamRef Stream,
152                                                        uint32_t Offset) {
153  return readCVRecordFromStream<SymbolKind>(Stream, Offset);
154}
155