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