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> {
enumerationllvm::yaml::ScalarEnumerationTraits32 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> {
outputllvm::yaml::ScalarTraits45 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
inputllvm::yaml::ScalarTraits59 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
mustQuotellvm::yaml::ScalarTraits70 static QuotingType mustQuote(StringRef) { return QuotingType::None; }
71 };
72
73 template <> struct ScalarTraits<IFSBitWidthType> {
outputllvm::yaml::ScalarTraits74 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
inputllvm::yaml::ScalarTraits88 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
mustQuotellvm::yaml::ScalarTraits99 static QuotingType mustQuote(StringRef) { return QuotingType::None; }
100 };
101
102 template <> struct MappingTraits<IFSTarget> {
mappingllvm::yaml::MappingTraits103 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> {
mappingllvm::yaml::MappingTraits116 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> {
mappingllvm::yaml::MappingTraits139 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> {
mappingllvm::yaml::MappingTraits152 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.
usesTriple(StringRef Buf)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
readIFSFromBuffer(StringRef Buf)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
writeIFSToOutputStream(raw_ostream & OS,const IFSStub & Stub)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
overrideIFSTarget(IFSStub & Stub,Optional<IFSArch> OverrideArch,Optional<IFSEndiannessType> OverrideEndianness,Optional<IFSBitWidthType> OverrideBitWidth,Optional<std::string> OverrideTriple)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
validateIFSTarget(IFSStub & Stub,bool ParseTriple)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
parseTriple(StringRef TripleStr)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
stripIFSTarget(IFSStub & Stub,bool StripTriple,bool StripArch,bool StripEndianness,bool StripBitWidth)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
filterIFSSyms(IFSStub & Stub,bool StripUndefined,const std::vector<std::string> & Exclude)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