1 //===- MinidumpYAML.cpp - Minidump YAMLIO implementation ------------------===// 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 #include "llvm/ObjectYAML/MinidumpYAML.h" 10 11 using namespace llvm; 12 using namespace llvm::MinidumpYAML; 13 using namespace llvm::minidump; 14 15 namespace { 16 class BlobAllocator { 17 public: 18 size_t tell() const { return NextOffset; } 19 20 size_t allocateCallback(size_t Size, 21 std::function<void(raw_ostream &)> Callback) { 22 size_t Offset = NextOffset; 23 NextOffset += Size; 24 Callbacks.push_back(std::move(Callback)); 25 return Offset; 26 } 27 28 size_t allocateBytes(ArrayRef<uint8_t> Data) { 29 return allocateCallback( 30 Data.size(), [Data](raw_ostream &OS) { OS << toStringRef(Data); }); 31 } 32 33 template <typename T> size_t allocateArray(ArrayRef<T> Data) { 34 return allocateBytes({reinterpret_cast<const uint8_t *>(Data.data()), 35 sizeof(T) * Data.size()}); 36 } 37 38 template <typename T> size_t allocateObject(const T &Data) { 39 return allocateArray(makeArrayRef(Data)); 40 } 41 42 void writeTo(raw_ostream &OS) const; 43 44 private: 45 size_t NextOffset = 0; 46 47 std::vector<std::function<void(raw_ostream &)>> Callbacks; 48 }; 49 } // namespace 50 51 void BlobAllocator::writeTo(raw_ostream &OS) const { 52 size_t BeginOffset = OS.tell(); 53 for (const auto &Callback : Callbacks) 54 Callback(OS); 55 assert(OS.tell() == BeginOffset + NextOffset && 56 "Callbacks wrote an unexpected number of bytes."); 57 (void)BeginOffset; 58 } 59 60 /// Perform an optional yaml-mapping of an endian-aware type EndianType. The 61 /// only purpose of this function is to avoid casting the Default value to the 62 /// endian type; 63 template <typename EndianType> 64 static inline void mapOptional(yaml::IO &IO, const char *Key, EndianType &Val, 65 typename EndianType::value_type Default) { 66 IO.mapOptional(Key, Val, EndianType(Default)); 67 } 68 69 /// Yaml-map an endian-aware type EndianType as some other type MapType. 70 template <typename MapType, typename EndianType> 71 static inline void mapRequiredAs(yaml::IO &IO, const char *Key, 72 EndianType &Val) { 73 MapType Mapped = static_cast<typename EndianType::value_type>(Val); 74 IO.mapRequired(Key, Mapped); 75 Val = static_cast<typename EndianType::value_type>(Mapped); 76 } 77 78 /// Perform an optional yaml-mapping of an endian-aware type EndianType as some 79 /// other type MapType. 80 template <typename MapType, typename EndianType> 81 static inline void mapOptionalAs(yaml::IO &IO, const char *Key, EndianType &Val, 82 MapType Default) { 83 MapType Mapped = static_cast<typename EndianType::value_type>(Val); 84 IO.mapOptional(Key, Mapped, Default); 85 Val = static_cast<typename EndianType::value_type>(Mapped); 86 } 87 88 namespace { 89 /// Return the appropriate yaml Hex type for a given endian-aware type. 90 template <typename EndianType> struct HexType; 91 template <> struct HexType<support::ulittle16_t> { using type = yaml::Hex16; }; 92 template <> struct HexType<support::ulittle32_t> { using type = yaml::Hex32; }; 93 template <> struct HexType<support::ulittle64_t> { using type = yaml::Hex64; }; 94 } // namespace 95 96 /// Yaml-map an endian-aware type as an appropriately-sized hex value. 97 template <typename EndianType> 98 static inline void mapRequiredHex(yaml::IO &IO, const char *Key, 99 EndianType &Val) { 100 mapRequiredAs<typename HexType<EndianType>::type>(IO, Key, Val); 101 } 102 103 /// Perform an optional yaml-mapping of an endian-aware type as an 104 /// appropriately-sized hex value. 105 template <typename EndianType> 106 static inline void mapOptionalHex(yaml::IO &IO, const char *Key, 107 EndianType &Val, 108 typename EndianType::value_type Default) { 109 mapOptionalAs<typename HexType<EndianType>::type>(IO, Key, Val, Default); 110 } 111 112 Stream::~Stream() = default; 113 114 Stream::StreamKind Stream::getKind(StreamType Type) { 115 switch (Type) { 116 case StreamType::SystemInfo: 117 return StreamKind::SystemInfo; 118 case StreamType::LinuxCPUInfo: 119 case StreamType::LinuxProcStatus: 120 case StreamType::LinuxLSBRelease: 121 case StreamType::LinuxCMDLine: 122 case StreamType::LinuxMaps: 123 case StreamType::LinuxProcStat: 124 case StreamType::LinuxProcUptime: 125 return StreamKind::TextContent; 126 default: 127 return StreamKind::RawContent; 128 } 129 } 130 131 std::unique_ptr<Stream> Stream::create(StreamType Type) { 132 StreamKind Kind = getKind(Type); 133 switch (Kind) { 134 case StreamKind::RawContent: 135 return llvm::make_unique<RawContentStream>(Type); 136 case StreamKind::SystemInfo: 137 return llvm::make_unique<SystemInfoStream>(); 138 case StreamKind::TextContent: 139 return llvm::make_unique<TextContentStream>(Type); 140 } 141 llvm_unreachable("Unhandled stream kind!"); 142 } 143 144 void yaml::ScalarEnumerationTraits<ProcessorArchitecture>::enumeration( 145 IO &IO, ProcessorArchitecture &Arch) { 146 #define HANDLE_MDMP_ARCH(CODE, NAME) \ 147 IO.enumCase(Arch, #NAME, ProcessorArchitecture::NAME); 148 #include "llvm/BinaryFormat/MinidumpConstants.def" 149 IO.enumFallback<Hex16>(Arch); 150 } 151 152 void yaml::ScalarEnumerationTraits<OSPlatform>::enumeration(IO &IO, 153 OSPlatform &Plat) { 154 #define HANDLE_MDMP_PLATFORM(CODE, NAME) \ 155 IO.enumCase(Plat, #NAME, OSPlatform::NAME); 156 #include "llvm/BinaryFormat/MinidumpConstants.def" 157 IO.enumFallback<Hex32>(Plat); 158 } 159 160 void yaml::ScalarEnumerationTraits<StreamType>::enumeration(IO &IO, 161 StreamType &Type) { 162 #define HANDLE_MDMP_STREAM_TYPE(CODE, NAME) \ 163 IO.enumCase(Type, #NAME, StreamType::NAME); 164 #include "llvm/BinaryFormat/MinidumpConstants.def" 165 IO.enumFallback<Hex32>(Type); 166 } 167 168 void yaml::MappingTraits<CPUInfo::ArmInfo>::mapping(IO &IO, 169 CPUInfo::ArmInfo &Info) { 170 mapRequiredHex(IO, "CPUID", Info.CPUID); 171 mapOptionalHex(IO, "ELF hwcaps", Info.ElfHWCaps, 0); 172 } 173 174 namespace { 175 template <std::size_t N> struct FixedSizeHex { 176 FixedSizeHex(uint8_t (&Storage)[N]) : Storage(Storage) {} 177 178 uint8_t (&Storage)[N]; 179 }; 180 } // namespace 181 182 namespace llvm { 183 namespace yaml { 184 template <std::size_t N> struct ScalarTraits<FixedSizeHex<N>> { 185 static void output(const FixedSizeHex<N> &Fixed, void *, raw_ostream &OS) { 186 OS << toHex(makeArrayRef(Fixed.Storage)); 187 } 188 189 static StringRef input(StringRef Scalar, void *, FixedSizeHex<N> &Fixed) { 190 if (!all_of(Scalar, isHexDigit)) 191 return "Invalid hex digit in input"; 192 if (Scalar.size() < 2 * N) 193 return "String too short"; 194 if (Scalar.size() > 2 * N) 195 return "String too long"; 196 copy(fromHex(Scalar), Fixed.Storage); 197 return ""; 198 } 199 200 static QuotingType mustQuote(StringRef S) { return QuotingType::None; } 201 }; 202 } // namespace yaml 203 } // namespace llvm 204 void yaml::MappingTraits<CPUInfo::OtherInfo>::mapping( 205 IO &IO, CPUInfo::OtherInfo &Info) { 206 FixedSizeHex<sizeof(Info.ProcessorFeatures)> Features(Info.ProcessorFeatures); 207 IO.mapRequired("Features", Features); 208 } 209 210 namespace { 211 /// A type which only accepts strings of a fixed size for yaml conversion. 212 template <std::size_t N> struct FixedSizeString { 213 FixedSizeString(char (&Storage)[N]) : Storage(Storage) {} 214 215 char (&Storage)[N]; 216 }; 217 } // namespace 218 219 namespace llvm { 220 namespace yaml { 221 template <std::size_t N> struct ScalarTraits<FixedSizeString<N>> { 222 static void output(const FixedSizeString<N> &Fixed, void *, raw_ostream &OS) { 223 OS << StringRef(Fixed.Storage, N); 224 } 225 226 static StringRef input(StringRef Scalar, void *, FixedSizeString<N> &Fixed) { 227 if (Scalar.size() < N) 228 return "String too short"; 229 if (Scalar.size() > N) 230 return "String too long"; 231 copy(Scalar, Fixed.Storage); 232 return ""; 233 } 234 235 static QuotingType mustQuote(StringRef S) { return needsQuotes(S); } 236 }; 237 } // namespace yaml 238 } // namespace llvm 239 240 void yaml::MappingTraits<CPUInfo::X86Info>::mapping(IO &IO, 241 CPUInfo::X86Info &Info) { 242 FixedSizeString<sizeof(Info.VendorID)> VendorID(Info.VendorID); 243 IO.mapRequired("Vendor ID", VendorID); 244 245 mapRequiredHex(IO, "Version Info", Info.VersionInfo); 246 mapRequiredHex(IO, "Feature Info", Info.FeatureInfo); 247 mapOptionalHex(IO, "AMD Extended Features", Info.AMDExtendedFeatures, 0); 248 } 249 250 static void streamMapping(yaml::IO &IO, RawContentStream &Stream) { 251 IO.mapOptional("Content", Stream.Content); 252 IO.mapOptional("Size", Stream.Size, Stream.Content.binary_size()); 253 } 254 255 static StringRef streamValidate(RawContentStream &Stream) { 256 if (Stream.Size.value < Stream.Content.binary_size()) 257 return "Stream size must be greater or equal to the content size"; 258 return ""; 259 } 260 261 static void streamMapping(yaml::IO &IO, SystemInfoStream &Stream) { 262 SystemInfo &Info = Stream.Info; 263 IO.mapRequired("Processor Arch", Info.ProcessorArch); 264 mapOptional(IO, "Processor Level", Info.ProcessorLevel, 0); 265 mapOptional(IO, "Processor Revision", Info.ProcessorRevision, 0); 266 IO.mapOptional("Number of Processors", Info.NumberOfProcessors, 0); 267 IO.mapOptional("Product type", Info.ProductType, 0); 268 mapOptional(IO, "Major Version", Info.MajorVersion, 0); 269 mapOptional(IO, "Minor Version", Info.MinorVersion, 0); 270 mapOptional(IO, "Build Number", Info.BuildNumber, 0); 271 IO.mapRequired("Platform ID", Info.PlatformId); 272 mapOptionalHex(IO, "CSD Version RVA", Info.CSDVersionRVA, 0); 273 mapOptionalHex(IO, "Suite Mask", Info.SuiteMask, 0); 274 mapOptionalHex(IO, "Reserved", Info.Reserved, 0); 275 switch (static_cast<ProcessorArchitecture>(Info.ProcessorArch)) { 276 case ProcessorArchitecture::X86: 277 case ProcessorArchitecture::AMD64: 278 IO.mapOptional("CPU", Info.CPU.X86); 279 break; 280 case ProcessorArchitecture::ARM: 281 case ProcessorArchitecture::ARM64: 282 IO.mapOptional("CPU", Info.CPU.Arm); 283 break; 284 default: 285 IO.mapOptional("CPU", Info.CPU.Other); 286 break; 287 } 288 } 289 290 static void streamMapping(yaml::IO &IO, TextContentStream &Stream) { 291 IO.mapOptional("Text", Stream.Text); 292 } 293 294 void yaml::MappingTraits<std::unique_ptr<Stream>>::mapping( 295 yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) { 296 StreamType Type; 297 if (IO.outputting()) 298 Type = S->Type; 299 IO.mapRequired("Type", Type); 300 301 if (!IO.outputting()) 302 S = MinidumpYAML::Stream::create(Type); 303 switch (S->Kind) { 304 case MinidumpYAML::Stream::StreamKind::RawContent: 305 streamMapping(IO, llvm::cast<RawContentStream>(*S)); 306 break; 307 case MinidumpYAML::Stream::StreamKind::SystemInfo: 308 streamMapping(IO, llvm::cast<SystemInfoStream>(*S)); 309 break; 310 case MinidumpYAML::Stream::StreamKind::TextContent: 311 streamMapping(IO, llvm::cast<TextContentStream>(*S)); 312 break; 313 } 314 } 315 316 StringRef yaml::MappingTraits<std::unique_ptr<Stream>>::validate( 317 yaml::IO &IO, std::unique_ptr<MinidumpYAML::Stream> &S) { 318 switch (S->Kind) { 319 case MinidumpYAML::Stream::StreamKind::RawContent: 320 return streamValidate(cast<RawContentStream>(*S)); 321 case MinidumpYAML::Stream::StreamKind::SystemInfo: 322 case MinidumpYAML::Stream::StreamKind::TextContent: 323 return ""; 324 } 325 llvm_unreachable("Fully covered switch above!"); 326 } 327 328 void yaml::MappingTraits<Object>::mapping(IO &IO, Object &O) { 329 IO.mapTag("!minidump", true); 330 mapOptionalHex(IO, "Signature", O.Header.Signature, Header::MagicSignature); 331 mapOptionalHex(IO, "Version", O.Header.Version, Header::MagicVersion); 332 mapOptionalHex(IO, "Flags", O.Header.Flags, 0); 333 IO.mapRequired("Streams", O.Streams); 334 } 335 336 static Directory layout(BlobAllocator &File, Stream &S) { 337 Directory Result; 338 Result.Type = S.Type; 339 Result.Location.RVA = File.tell(); 340 switch (S.Kind) { 341 case Stream::StreamKind::RawContent: { 342 RawContentStream &Raw = cast<RawContentStream>(S); 343 File.allocateCallback(Raw.Size, [&Raw](raw_ostream &OS) { 344 Raw.Content.writeAsBinary(OS); 345 assert(Raw.Content.binary_size() <= Raw.Size); 346 OS << std::string(Raw.Size - Raw.Content.binary_size(), '\0'); 347 }); 348 break; 349 } 350 case Stream::StreamKind::SystemInfo: 351 File.allocateObject(cast<SystemInfoStream>(S).Info); 352 break; 353 case Stream::StreamKind::TextContent: 354 File.allocateArray(arrayRefFromStringRef(cast<TextContentStream>(S).Text)); 355 break; 356 } 357 Result.Location.DataSize = File.tell() - Result.Location.RVA; 358 return Result; 359 } 360 361 void MinidumpYAML::writeAsBinary(Object &Obj, raw_ostream &OS) { 362 BlobAllocator File; 363 File.allocateObject(Obj.Header); 364 365 std::vector<Directory> StreamDirectory(Obj.Streams.size()); 366 Obj.Header.StreamDirectoryRVA = 367 File.allocateArray(makeArrayRef(StreamDirectory)); 368 Obj.Header.NumberOfStreams = StreamDirectory.size(); 369 370 for (auto &Stream : enumerate(Obj.Streams)) 371 StreamDirectory[Stream.index()] = layout(File, *Stream.value()); 372 373 File.writeTo(OS); 374 } 375 376 Error MinidumpYAML::writeAsBinary(StringRef Yaml, raw_ostream &OS) { 377 yaml::Input Input(Yaml); 378 Object Obj; 379 Input >> Obj; 380 if (std::error_code EC = Input.error()) 381 return errorCodeToError(EC); 382 383 writeAsBinary(Obj, OS); 384 return Error::success(); 385 } 386