1 //===- IFSHandler.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/InterfaceStub/IFSHandler.h"
10 #include "llvm/ADT/STLExtras.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/ADT/StringSwitch.h"
13 #include "llvm/ADT/Triple.h"
14 #include "llvm/BinaryFormat/ELF.h"
15 #include "llvm/InterfaceStub/IFSStub.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/GlobPattern.h"
18 #include "llvm/Support/LineIterator.h"
19 #include "llvm/Support/YAMLTraits.h"
20 #include <functional>
21 
22 using namespace llvm;
23 using namespace llvm::ifs;
24 
25 LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol)
26 
27 namespace llvm {
28 namespace yaml {
29 
30 /// YAML traits for ELFSymbolType.
31 template <> struct ScalarEnumerationTraits<IFSSymbolType> {
32   static void enumeration(IO &IO, IFSSymbolType &SymbolType) {
33     IO.enumCase(SymbolType, "NoType", IFSSymbolType::NoType);
34     IO.enumCase(SymbolType, "Func", IFSSymbolType::Func);
35     IO.enumCase(SymbolType, "Object", IFSSymbolType::Object);
36     IO.enumCase(SymbolType, "TLS", IFSSymbolType::TLS);
37     IO.enumCase(SymbolType, "Unknown", IFSSymbolType::Unknown);
38     // Treat other symbol types as noise, and map to Unknown.
39     if (!IO.outputting() && IO.matchEnumFallback())
40       SymbolType = IFSSymbolType::Unknown;
41   }
42 };
43 
44 template <> struct ScalarTraits<IFSEndiannessType> {
45   static void output(const IFSEndiannessType &Value, void *,
46                      llvm::raw_ostream &Out) {
47     switch (Value) {
48     case IFSEndiannessType::Big:
49       Out << "big";
50       break;
51     case IFSEndiannessType::Little:
52       Out << "little";
53       break;
54     default:
55       llvm_unreachable("Unsupported endianness");
56     }
57   }
58 
59   static StringRef input(StringRef Scalar, void *, IFSEndiannessType &Value) {
60     Value = StringSwitch<IFSEndiannessType>(Scalar)
61                 .Case("big", IFSEndiannessType::Big)
62                 .Case("little", IFSEndiannessType::Little)
63                 .Default(IFSEndiannessType::Unknown);
64     if (Value == IFSEndiannessType::Unknown) {
65       return "Unsupported endianness";
66     }
67     return StringRef();
68   }
69 
70   static QuotingType mustQuote(StringRef) { return QuotingType::None; }
71 };
72 
73 template <> struct ScalarTraits<IFSBitWidthType> {
74   static void output(const IFSBitWidthType &Value, void *,
75                      llvm::raw_ostream &Out) {
76     switch (Value) {
77     case IFSBitWidthType::IFS32:
78       Out << "32";
79       break;
80     case IFSBitWidthType::IFS64:
81       Out << "64";
82       break;
83     default:
84       llvm_unreachable("Unsupported bit width");
85     }
86   }
87 
88   static StringRef input(StringRef Scalar, void *, IFSBitWidthType &Value) {
89     Value = StringSwitch<IFSBitWidthType>(Scalar)
90                 .Case("32", IFSBitWidthType::IFS32)
91                 .Case("64", IFSBitWidthType::IFS64)
92                 .Default(IFSBitWidthType::Unknown);
93     if (Value == IFSBitWidthType::Unknown) {
94       return "Unsupported bit width";
95     }
96     return StringRef();
97   }
98 
99   static QuotingType mustQuote(StringRef) { return QuotingType::None; }
100 };
101 
102 template <> struct MappingTraits<IFSTarget> {
103   static void mapping(IO &IO, IFSTarget &Target) {
104     IO.mapOptional("ObjectFormat", Target.ObjectFormat);
105     IO.mapOptional("Arch", Target.ArchString);
106     IO.mapOptional("Endianness", Target.Endianness);
107     IO.mapOptional("BitWidth", Target.BitWidth);
108   }
109 
110   // Compacts symbol information into a single line.
111   static const bool flow = true; // NOLINT(readability-identifier-naming)
112 };
113 
114 /// YAML traits for ELFSymbol.
115 template <> struct MappingTraits<IFSSymbol> {
116   static void mapping(IO &IO, IFSSymbol &Symbol) {
117     IO.mapRequired("Name", Symbol.Name);
118     IO.mapRequired("Type", Symbol.Type);
119     // The need for symbol size depends on the symbol type.
120     if (Symbol.Type == IFSSymbolType::NoType) {
121       // Size is None, so we are reading it in, or it is non 0 so we
122       // should emit it.
123       if (!Symbol.Size || *Symbol.Size)
124         IO.mapOptional("Size", Symbol.Size);
125     } else if (Symbol.Type != IFSSymbolType::Func) {
126       IO.mapOptional("Size", Symbol.Size);
127     }
128     IO.mapOptional("Undefined", Symbol.Undefined, false);
129     IO.mapOptional("Weak", Symbol.Weak, false);
130     IO.mapOptional("Warning", Symbol.Warning);
131   }
132 
133   // Compacts symbol information into a single line.
134   static const bool flow = true; // NOLINT(readability-identifier-naming)
135 };
136 
137 /// YAML traits for ELFStub objects.
138 template <> struct MappingTraits<IFSStub> {
139   static void mapping(IO &IO, IFSStub &Stub) {
140     if (!IO.mapTag("!ifs-v1", true))
141       IO.setError("Not a .tbe YAML file.");
142     IO.mapRequired("IfsVersion", Stub.IfsVersion);
143     IO.mapOptional("SoName", Stub.SoName);
144     IO.mapOptional("Target", Stub.Target);
145     IO.mapOptional("NeededLibs", Stub.NeededLibs);
146     IO.mapRequired("Symbols", Stub.Symbols);
147   }
148 };
149 
150 /// YAML traits for ELFStubTriple objects.
151 template <> struct MappingTraits<IFSStubTriple> {
152   static void mapping(IO &IO, IFSStubTriple &Stub) {
153     if (!IO.mapTag("!ifs-v1", true))
154       IO.setError("Not a .tbe YAML file.");
155     IO.mapRequired("IfsVersion", Stub.IfsVersion);
156     IO.mapOptional("SoName", Stub.SoName);
157     IO.mapOptional("Target", Stub.Target.Triple);
158     IO.mapOptional("NeededLibs", Stub.NeededLibs);
159     IO.mapRequired("Symbols", Stub.Symbols);
160   }
161 };
162 } // end namespace yaml
163 } // end namespace llvm
164 
165 /// Attempt to determine if a Text stub uses target triple.
166 bool usesTriple(StringRef Buf) {
167   for (line_iterator I(MemoryBufferRef(Buf, "ELFStub")); !I.is_at_eof(); ++I) {
168     StringRef Line = (*I).trim();
169     if (Line.startswith("Target:")) {
170       if (Line == "Target:" || Line.contains("{")) {
171         return false;
172       }
173     }
174   }
175   return true;
176 }
177 
178 Expected<std::unique_ptr<IFSStub>> ifs::readIFSFromBuffer(StringRef Buf) {
179   yaml::Input YamlIn(Buf);
180   std::unique_ptr<IFSStubTriple> Stub(new IFSStubTriple());
181   if (usesTriple(Buf)) {
182     YamlIn >> *Stub;
183   } else {
184     YamlIn >> *static_cast<IFSStub *>(Stub.get());
185   }
186   if (std::error_code Err = YamlIn.error()) {
187     return createStringError(Err, "YAML failed reading as IFS");
188   }
189 
190   if (Stub->IfsVersion > IFSVersionCurrent)
191     return make_error<StringError>(
192         "IFS version " + Stub->IfsVersion.getAsString() + " is unsupported.",
193         std::make_error_code(std::errc::invalid_argument));
194   if (Stub->Target.ArchString) {
195     Stub->Target.Arch =
196         ELF::convertArchNameToEMachine(*Stub->Target.ArchString);
197   }
198   return std::move(Stub);
199 }
200 
201 Error ifs::writeIFSToOutputStream(raw_ostream &OS, const IFSStub &Stub) {
202   yaml::Output YamlOut(OS, nullptr, /*WrapColumn =*/0);
203   std::unique_ptr<IFSStubTriple> CopyStub(new IFSStubTriple(Stub));
204   if (Stub.Target.Arch) {
205     CopyStub->Target.ArchString =
206         std::string(ELF::convertEMachineToArchName(Stub.Target.Arch.value()));
207   }
208   IFSTarget Target = Stub.Target;
209 
210   if (CopyStub->Target.Triple ||
211       (!CopyStub->Target.ArchString && !CopyStub->Target.Endianness &&
212        !CopyStub->Target.BitWidth))
213     YamlOut << *CopyStub;
214   else
215     YamlOut << *static_cast<IFSStub *>(CopyStub.get());
216   return Error::success();
217 }
218 
219 Error ifs::overrideIFSTarget(IFSStub &Stub, Optional<IFSArch> OverrideArch,
220                              Optional<IFSEndiannessType> OverrideEndianness,
221                              Optional<IFSBitWidthType> OverrideBitWidth,
222                              Optional<std::string> OverrideTriple) {
223   std::error_code OverrideEC(1, std::generic_category());
224   if (OverrideArch) {
225     if (Stub.Target.Arch && Stub.Target.Arch.value() != OverrideArch.value()) {
226       return make_error<StringError>(
227           "Supplied Arch conflicts with the text stub", OverrideEC);
228     }
229     Stub.Target.Arch = OverrideArch.value();
230   }
231   if (OverrideEndianness) {
232     if (Stub.Target.Endianness &&
233         Stub.Target.Endianness.value() != OverrideEndianness.value()) {
234       return make_error<StringError>(
235           "Supplied Endianness conflicts with the text stub", OverrideEC);
236     }
237     Stub.Target.Endianness = OverrideEndianness.value();
238   }
239   if (OverrideBitWidth) {
240     if (Stub.Target.BitWidth &&
241         Stub.Target.BitWidth.value() != OverrideBitWidth.value()) {
242       return make_error<StringError>(
243           "Supplied BitWidth conflicts with the text stub", OverrideEC);
244     }
245     Stub.Target.BitWidth = OverrideBitWidth.value();
246   }
247   if (OverrideTriple) {
248     if (Stub.Target.Triple &&
249         Stub.Target.Triple.value() != OverrideTriple.value()) {
250       return make_error<StringError>(
251           "Supplied Triple conflicts with the text stub", OverrideEC);
252     }
253     Stub.Target.Triple = OverrideTriple.value();
254   }
255   return Error::success();
256 }
257 
258 Error ifs::validateIFSTarget(IFSStub &Stub, bool ParseTriple) {
259   std::error_code ValidationEC(1, std::generic_category());
260   if (Stub.Target.Triple) {
261     if (Stub.Target.Arch || Stub.Target.BitWidth || Stub.Target.Endianness ||
262         Stub.Target.ObjectFormat) {
263       return make_error<StringError>(
264           "Target triple cannot be used simultaneously with ELF target format",
265           ValidationEC);
266     }
267     if (ParseTriple) {
268       IFSTarget TargetFromTriple = parseTriple(*Stub.Target.Triple);
269       Stub.Target.Arch = TargetFromTriple.Arch;
270       Stub.Target.BitWidth = TargetFromTriple.BitWidth;
271       Stub.Target.Endianness = TargetFromTriple.Endianness;
272     }
273     return Error::success();
274   }
275   if (!Stub.Target.Arch || !Stub.Target.BitWidth || !Stub.Target.Endianness) {
276     // TODO: unify the error message.
277     if (!Stub.Target.Arch) {
278       return make_error<StringError>("Arch is not defined in the text stub",
279                                      ValidationEC);
280     }
281     if (!Stub.Target.BitWidth) {
282       return make_error<StringError>("BitWidth is not defined in the text stub",
283                                      ValidationEC);
284     }
285     if (!Stub.Target.Endianness) {
286       return make_error<StringError>(
287           "Endianness is not defined in the text stub", ValidationEC);
288     }
289   }
290   return Error::success();
291 }
292 
293 IFSTarget ifs::parseTriple(StringRef TripleStr) {
294   Triple IFSTriple(TripleStr);
295   IFSTarget RetTarget;
296   // TODO: Implement a Triple Arch enum to e_machine map.
297   switch (IFSTriple.getArch()) {
298   case Triple::ArchType::aarch64:
299     RetTarget.Arch = (IFSArch)ELF::EM_AARCH64;
300     break;
301   case Triple::ArchType::x86_64:
302     RetTarget.Arch = (IFSArch)ELF::EM_X86_64;
303     break;
304   default:
305     RetTarget.Arch = (IFSArch)ELF::EM_NONE;
306   }
307   RetTarget.Endianness = IFSTriple.isLittleEndian() ? IFSEndiannessType::Little
308                                                     : IFSEndiannessType::Big;
309   RetTarget.BitWidth =
310       IFSTriple.isArch64Bit() ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32;
311   return RetTarget;
312 }
313 
314 void ifs::stripIFSTarget(IFSStub &Stub, bool StripTriple, bool StripArch,
315                          bool StripEndianness, bool StripBitWidth) {
316   if (StripTriple || StripArch) {
317     Stub.Target.Arch.reset();
318     Stub.Target.ArchString.reset();
319   }
320   if (StripTriple || StripEndianness) {
321     Stub.Target.Endianness.reset();
322   }
323   if (StripTriple || StripBitWidth) {
324     Stub.Target.BitWidth.reset();
325   }
326   if (StripTriple) {
327     Stub.Target.Triple.reset();
328   }
329   if (!Stub.Target.Arch && !Stub.Target.BitWidth && !Stub.Target.Endianness) {
330     Stub.Target.ObjectFormat.reset();
331   }
332 }
333 
334 Error ifs::filterIFSSyms(IFSStub &Stub, bool StripUndefined,
335                          const std::vector<std::string> &Exclude) {
336   std::function<bool(const IFSSymbol &)> Filter = [](const IFSSymbol &) {
337     return false;
338   };
339 
340   if (StripUndefined) {
341     Filter = [Filter](const IFSSymbol &Sym) {
342       return Sym.Undefined || Filter(Sym);
343     };
344   }
345 
346   for (StringRef Glob : Exclude) {
347     Expected<llvm::GlobPattern> PatternOrErr = llvm::GlobPattern::create(Glob);
348     if (!PatternOrErr)
349       return PatternOrErr.takeError();
350     Filter = [Pattern = *PatternOrErr, Filter](const IFSSymbol &Sym) {
351       return Pattern.match(Sym.Name) || Filter(Sym);
352     };
353   }
354 
355   llvm::erase_if(Stub.Symbols, Filter);
356 
357   return Error::success();
358 }
359