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