1//===- TBEHandler.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#include "llvm/TextAPI/ELF/TBEHandler.h"
10#include "llvm/ADT/StringSwitch.h"
11#include "llvm/ADT/StringRef.h"
12#include "llvm/Support/Error.h"
13#include "llvm/Support/YAMLTraits.h"
14#include "llvm/TextAPI/ELF/ELFStub.h"
15
16using namespace llvm;
17using namespace llvm::elfabi;
18
19LLVM_YAML_STRONG_TYPEDEF(ELFArch, ELFArchMapper)
20
21namespace llvm {
22namespace yaml {
23
24/// YAML traits for ELFSymbolType.
25template <> struct ScalarEnumerationTraits<ELFSymbolType> {
26  static void enumeration(IO &IO, ELFSymbolType &SymbolType) {
27    IO.enumCase(SymbolType, "NoType", ELFSymbolType::NoType);
28    IO.enumCase(SymbolType, "Func", ELFSymbolType::Func);
29    IO.enumCase(SymbolType, "Object", ELFSymbolType::Object);
30    IO.enumCase(SymbolType, "TLS", ELFSymbolType::TLS);
31    IO.enumCase(SymbolType, "Unknown", ELFSymbolType::Unknown);
32    // Treat other symbol types as noise, and map to Unknown.
33    if (!IO.outputting() && IO.matchEnumFallback())
34      SymbolType = ELFSymbolType::Unknown;
35  }
36};
37
38/// YAML traits for ELFArch.
39template <> struct ScalarTraits<ELFArchMapper> {
40  static void output(const ELFArchMapper &Value, void *,
41                     llvm::raw_ostream &Out) {
42    // Map from integer to architecture string.
43    switch (Value) {
44    case (ELFArch)ELF::EM_X86_64:
45      Out << "x86_64";
46      break;
47    case (ELFArch)ELF::EM_AARCH64:
48      Out << "AArch64";
49      break;
50    case (ELFArch)ELF::EM_NONE:
51    default:
52      Out << "Unknown";
53    }
54  }
55
56  static StringRef input(StringRef Scalar, void *, ELFArchMapper &Value) {
57    // Map from architecture string to integer.
58    Value = StringSwitch<ELFArch>(Scalar)
59                .Case("x86_64", ELF::EM_X86_64)
60                .Case("AArch64", ELF::EM_AARCH64)
61                .Case("Unknown", ELF::EM_NONE)
62                .Default(ELF::EM_NONE);
63
64    // Returning empty StringRef indicates successful parse.
65    return StringRef();
66  }
67
68  // Don't place quotation marks around architecture value.
69  static QuotingType mustQuote(StringRef) { return QuotingType::None; }
70};
71
72/// YAML traits for TbeVersion.
73template <> struct ScalarTraits<VersionTuple> {
74  static void output(const VersionTuple &Value, void *,
75                     llvm::raw_ostream &Out) {
76    Out << Value.getAsString();
77  }
78
79  static StringRef input(StringRef Scalar, void *, VersionTuple &Value) {
80    if (Value.tryParse(Scalar))
81      return StringRef("Can't parse version: invalid version format.");
82
83    if (Value > TBEVersionCurrent)
84      return StringRef("Unsupported TBE version.");
85
86    // Returning empty StringRef indicates successful parse.
87    return StringRef();
88  }
89
90  // Don't place quotation marks around version value.
91  static QuotingType mustQuote(StringRef) { return QuotingType::None; }
92};
93
94/// YAML traits for ELFSymbol.
95template <> struct MappingTraits<ELFSymbol> {
96  static void mapping(IO &IO, ELFSymbol &Symbol) {
97    IO.mapRequired("Type", Symbol.Type);
98    // The need for symbol size depends on the symbol type.
99    if (Symbol.Type == ELFSymbolType::NoType) {
100      IO.mapOptional("Size", Symbol.Size, (uint64_t)0);
101    } else if (Symbol.Type == ELFSymbolType::Func) {
102      Symbol.Size = 0;
103    } else {
104      IO.mapRequired("Size", Symbol.Size);
105    }
106    IO.mapOptional("Undefined", Symbol.Undefined, false);
107    IO.mapOptional("Weak", Symbol.Weak, false);
108    IO.mapOptional("Warning", Symbol.Warning);
109  }
110
111  // Compacts symbol information into a single line.
112  static const bool flow = true;
113};
114
115/// YAML traits for set of ELFSymbols.
116template <> struct CustomMappingTraits<std::set<ELFSymbol>> {
117  static void inputOne(IO &IO, StringRef Key, std::set<ELFSymbol> &Set) {
118    ELFSymbol Sym(Key.str());
119    IO.mapRequired(Key.str().c_str(), Sym);
120    Set.insert(Sym);
121  }
122
123  static void output(IO &IO, std::set<ELFSymbol> &Set) {
124    for (auto &Sym : Set)
125      IO.mapRequired(Sym.Name.c_str(), const_cast<ELFSymbol &>(Sym));
126  }
127};
128
129/// YAML traits for ELFStub objects.
130template <> struct MappingTraits<ELFStub> {
131  static void mapping(IO &IO, ELFStub &Stub) {
132    if (!IO.mapTag("!tapi-tbe", true))
133      IO.setError("Not a .tbe YAML file.");
134    IO.mapRequired("TbeVersion", Stub.TbeVersion);
135    IO.mapOptional("SoName", Stub.SoName);
136    IO.mapRequired("Arch", (ELFArchMapper &)Stub.Arch);
137    IO.mapOptional("NeededLibs", Stub.NeededLibs);
138    IO.mapRequired("Symbols", Stub.Symbols);
139  }
140};
141
142} // end namespace yaml
143} // end namespace llvm
144
145Expected<std::unique_ptr<ELFStub>> elfabi::readTBEFromBuffer(StringRef Buf) {
146  yaml::Input YamlIn(Buf);
147  std::unique_ptr<ELFStub> Stub(new ELFStub());
148  YamlIn >> *Stub;
149  if (std::error_code Err = YamlIn.error())
150    return createStringError(Err, "YAML failed reading as TBE");
151
152  return std::move(Stub);
153}
154
155Error elfabi::writeTBEToOutputStream(raw_ostream &OS, const ELFStub &Stub) {
156  yaml::Output YamlOut(OS, NULL, /*WrapColumn =*/0);
157
158  YamlOut << const_cast<ELFStub &>(Stub);
159  return Error::success();
160}
161