360 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- IFSHandler.cpp -----------------------------------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===-----------------------------------------------------------------------===/
 | |
| 
 | |
| #include "llvm/InterfaceStub/IFSHandler.h"
 | |
| #include "llvm/ADT/STLExtras.h"
 | |
| #include "llvm/ADT/StringRef.h"
 | |
| #include "llvm/ADT/StringSwitch.h"
 | |
| #include "llvm/ADT/Triple.h"
 | |
| #include "llvm/BinaryFormat/ELF.h"
 | |
| #include "llvm/InterfaceStub/IFSStub.h"
 | |
| #include "llvm/Support/Error.h"
 | |
| #include "llvm/Support/GlobPattern.h"
 | |
| #include "llvm/Support/LineIterator.h"
 | |
| #include "llvm/Support/YAMLTraits.h"
 | |
| #include <functional>
 | |
| 
 | |
| using namespace llvm;
 | |
| using namespace llvm::ifs;
 | |
| 
 | |
| LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol)
 | |
| 
 | |
| namespace llvm {
 | |
| namespace yaml {
 | |
| 
 | |
| /// YAML traits for ELFSymbolType.
 | |
| template <> struct ScalarEnumerationTraits<IFSSymbolType> {
 | |
|   static void enumeration(IO &IO, IFSSymbolType &SymbolType) {
 | |
|     IO.enumCase(SymbolType, "NoType", IFSSymbolType::NoType);
 | |
|     IO.enumCase(SymbolType, "Func", IFSSymbolType::Func);
 | |
|     IO.enumCase(SymbolType, "Object", IFSSymbolType::Object);
 | |
|     IO.enumCase(SymbolType, "TLS", IFSSymbolType::TLS);
 | |
|     IO.enumCase(SymbolType, "Unknown", IFSSymbolType::Unknown);
 | |
|     // Treat other symbol types as noise, and map to Unknown.
 | |
|     if (!IO.outputting() && IO.matchEnumFallback())
 | |
|       SymbolType = IFSSymbolType::Unknown;
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <> struct ScalarTraits<IFSEndiannessType> {
 | |
|   static void output(const IFSEndiannessType &Value, void *,
 | |
|                      llvm::raw_ostream &Out) {
 | |
|     switch (Value) {
 | |
|     case IFSEndiannessType::Big:
 | |
|       Out << "big";
 | |
|       break;
 | |
|     case IFSEndiannessType::Little:
 | |
|       Out << "little";
 | |
|       break;
 | |
|     default:
 | |
|       llvm_unreachable("Unsupported endianness");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   static StringRef input(StringRef Scalar, void *, IFSEndiannessType &Value) {
 | |
|     Value = StringSwitch<IFSEndiannessType>(Scalar)
 | |
|                 .Case("big", IFSEndiannessType::Big)
 | |
|                 .Case("little", IFSEndiannessType::Little)
 | |
|                 .Default(IFSEndiannessType::Unknown);
 | |
|     if (Value == IFSEndiannessType::Unknown) {
 | |
|       return "Unsupported endianness";
 | |
|     }
 | |
|     return StringRef();
 | |
|   }
 | |
| 
 | |
|   static QuotingType mustQuote(StringRef) { return QuotingType::None; }
 | |
| };
 | |
| 
 | |
| template <> struct ScalarTraits<IFSBitWidthType> {
 | |
|   static void output(const IFSBitWidthType &Value, void *,
 | |
|                      llvm::raw_ostream &Out) {
 | |
|     switch (Value) {
 | |
|     case IFSBitWidthType::IFS32:
 | |
|       Out << "32";
 | |
|       break;
 | |
|     case IFSBitWidthType::IFS64:
 | |
|       Out << "64";
 | |
|       break;
 | |
|     default:
 | |
|       llvm_unreachable("Unsupported bit width");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   static StringRef input(StringRef Scalar, void *, IFSBitWidthType &Value) {
 | |
|     Value = StringSwitch<IFSBitWidthType>(Scalar)
 | |
|                 .Case("32", IFSBitWidthType::IFS32)
 | |
|                 .Case("64", IFSBitWidthType::IFS64)
 | |
|                 .Default(IFSBitWidthType::Unknown);
 | |
|     if (Value == IFSBitWidthType::Unknown) {
 | |
|       return "Unsupported bit width";
 | |
|     }
 | |
|     return StringRef();
 | |
|   }
 | |
| 
 | |
|   static QuotingType mustQuote(StringRef) { return QuotingType::None; }
 | |
| };
 | |
| 
 | |
| template <> struct MappingTraits<IFSTarget> {
 | |
|   static void mapping(IO &IO, IFSTarget &Target) {
 | |
|     IO.mapOptional("ObjectFormat", Target.ObjectFormat);
 | |
|     IO.mapOptional("Arch", Target.ArchString);
 | |
|     IO.mapOptional("Endianness", Target.Endianness);
 | |
|     IO.mapOptional("BitWidth", Target.BitWidth);
 | |
|   }
 | |
| 
 | |
|   // Compacts symbol information into a single line.
 | |
|   static const bool flow = true; // NOLINT(readability-identifier-naming)
 | |
| };
 | |
| 
 | |
| /// YAML traits for ELFSymbol.
 | |
| template <> struct MappingTraits<IFSSymbol> {
 | |
|   static void mapping(IO &IO, IFSSymbol &Symbol) {
 | |
|     IO.mapRequired("Name", Symbol.Name);
 | |
|     IO.mapRequired("Type", Symbol.Type);
 | |
|     // The need for symbol size depends on the symbol type.
 | |
|     if (Symbol.Type == IFSSymbolType::NoType) {
 | |
|       // Size is None, so we are reading it in, or it is non 0 so we
 | |
|       // should emit it.
 | |
|       if (!Symbol.Size || *Symbol.Size)
 | |
|         IO.mapOptional("Size", Symbol.Size);
 | |
|     } else if (Symbol.Type != IFSSymbolType::Func) {
 | |
|       IO.mapOptional("Size", Symbol.Size);
 | |
|     }
 | |
|     IO.mapOptional("Undefined", Symbol.Undefined, false);
 | |
|     IO.mapOptional("Weak", Symbol.Weak, false);
 | |
|     IO.mapOptional("Warning", Symbol.Warning);
 | |
|   }
 | |
| 
 | |
|   // Compacts symbol information into a single line.
 | |
|   static const bool flow = true; // NOLINT(readability-identifier-naming)
 | |
| };
 | |
| 
 | |
| /// YAML traits for ELFStub objects.
 | |
| template <> struct MappingTraits<IFSStub> {
 | |
|   static void mapping(IO &IO, IFSStub &Stub) {
 | |
|     if (!IO.mapTag("!ifs-v1", true))
 | |
|       IO.setError("Not a .tbe YAML file.");
 | |
|     IO.mapRequired("IfsVersion", Stub.IfsVersion);
 | |
|     IO.mapOptional("SoName", Stub.SoName);
 | |
|     IO.mapOptional("Target", Stub.Target);
 | |
|     IO.mapOptional("NeededLibs", Stub.NeededLibs);
 | |
|     IO.mapRequired("Symbols", Stub.Symbols);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /// YAML traits for ELFStubTriple objects.
 | |
| template <> struct MappingTraits<IFSStubTriple> {
 | |
|   static void mapping(IO &IO, IFSStubTriple &Stub) {
 | |
|     if (!IO.mapTag("!ifs-v1", true))
 | |
|       IO.setError("Not a .tbe YAML file.");
 | |
|     IO.mapRequired("IfsVersion", Stub.IfsVersion);
 | |
|     IO.mapOptional("SoName", Stub.SoName);
 | |
|     IO.mapOptional("Target", Stub.Target.Triple);
 | |
|     IO.mapOptional("NeededLibs", Stub.NeededLibs);
 | |
|     IO.mapRequired("Symbols", Stub.Symbols);
 | |
|   }
 | |
| };
 | |
| } // end namespace yaml
 | |
| } // end namespace llvm
 | |
| 
 | |
| /// Attempt to determine if a Text stub uses target triple.
 | |
| bool usesTriple(StringRef Buf) {
 | |
|   for (line_iterator I(MemoryBufferRef(Buf, "ELFStub")); !I.is_at_eof(); ++I) {
 | |
|     StringRef Line = (*I).trim();
 | |
|     if (Line.startswith("Target:")) {
 | |
|       if (Line == "Target:" || Line.contains("{")) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| Expected<std::unique_ptr<IFSStub>> ifs::readIFSFromBuffer(StringRef Buf) {
 | |
|   yaml::Input YamlIn(Buf);
 | |
|   std::unique_ptr<IFSStubTriple> Stub(new IFSStubTriple());
 | |
|   if (usesTriple(Buf)) {
 | |
|     YamlIn >> *Stub;
 | |
|   } else {
 | |
|     YamlIn >> *static_cast<IFSStub *>(Stub.get());
 | |
|   }
 | |
|   if (std::error_code Err = YamlIn.error()) {
 | |
|     return createStringError(Err, "YAML failed reading as IFS");
 | |
|   }
 | |
| 
 | |
|   if (Stub->IfsVersion > IFSVersionCurrent)
 | |
|     return make_error<StringError>(
 | |
|         "IFS version " + Stub->IfsVersion.getAsString() + " is unsupported.",
 | |
|         std::make_error_code(std::errc::invalid_argument));
 | |
|   if (Stub->Target.ArchString) {
 | |
|     Stub->Target.Arch =
 | |
|         ELF::convertArchNameToEMachine(Stub->Target.ArchString.getValue());
 | |
|   }
 | |
|   return std::move(Stub);
 | |
| }
 | |
| 
 | |
| Error ifs::writeIFSToOutputStream(raw_ostream &OS, const IFSStub &Stub) {
 | |
|   yaml::Output YamlOut(OS, nullptr, /*WrapColumn =*/0);
 | |
|   std::unique_ptr<IFSStubTriple> CopyStub(new IFSStubTriple(Stub));
 | |
|   if (Stub.Target.Arch) {
 | |
|     CopyStub->Target.ArchString = std::string(
 | |
|         ELF::convertEMachineToArchName(Stub.Target.Arch.getValue()));
 | |
|   }
 | |
|   IFSTarget Target = Stub.Target;
 | |
| 
 | |
|   if (CopyStub->Target.Triple ||
 | |
|       (!CopyStub->Target.ArchString && !CopyStub->Target.Endianness &&
 | |
|        !CopyStub->Target.BitWidth))
 | |
|     YamlOut << *CopyStub;
 | |
|   else
 | |
|     YamlOut << *static_cast<IFSStub *>(CopyStub.get());
 | |
|   return Error::success();
 | |
| }
 | |
| 
 | |
| Error ifs::overrideIFSTarget(IFSStub &Stub, Optional<IFSArch> OverrideArch,
 | |
|                              Optional<IFSEndiannessType> OverrideEndianness,
 | |
|                              Optional<IFSBitWidthType> OverrideBitWidth,
 | |
|                              Optional<std::string> OverrideTriple) {
 | |
|   std::error_code OverrideEC(1, std::generic_category());
 | |
|   if (OverrideArch) {
 | |
|     if (Stub.Target.Arch &&
 | |
|         Stub.Target.Arch.getValue() != OverrideArch.getValue()) {
 | |
|       return make_error<StringError>(
 | |
|           "Supplied Arch conflicts with the text stub", OverrideEC);
 | |
|     }
 | |
|     Stub.Target.Arch = OverrideArch.getValue();
 | |
|   }
 | |
|   if (OverrideEndianness) {
 | |
|     if (Stub.Target.Endianness &&
 | |
|         Stub.Target.Endianness.getValue() != OverrideEndianness.getValue()) {
 | |
|       return make_error<StringError>(
 | |
|           "Supplied Endianness conflicts with the text stub", OverrideEC);
 | |
|     }
 | |
|     Stub.Target.Endianness = OverrideEndianness.getValue();
 | |
|   }
 | |
|   if (OverrideBitWidth) {
 | |
|     if (Stub.Target.BitWidth &&
 | |
|         Stub.Target.BitWidth.getValue() != OverrideBitWidth.getValue()) {
 | |
|       return make_error<StringError>(
 | |
|           "Supplied BitWidth conflicts with the text stub", OverrideEC);
 | |
|     }
 | |
|     Stub.Target.BitWidth = OverrideBitWidth.getValue();
 | |
|   }
 | |
|   if (OverrideTriple) {
 | |
|     if (Stub.Target.Triple &&
 | |
|         Stub.Target.Triple.getValue() != OverrideTriple.getValue()) {
 | |
|       return make_error<StringError>(
 | |
|           "Supplied Triple conflicts with the text stub", OverrideEC);
 | |
|     }
 | |
|     Stub.Target.Triple = OverrideTriple.getValue();
 | |
|   }
 | |
|   return Error::success();
 | |
| }
 | |
| 
 | |
| Error ifs::validateIFSTarget(IFSStub &Stub, bool ParseTriple) {
 | |
|   std::error_code ValidationEC(1, std::generic_category());
 | |
|   if (Stub.Target.Triple) {
 | |
|     if (Stub.Target.Arch || Stub.Target.BitWidth || Stub.Target.Endianness ||
 | |
|         Stub.Target.ObjectFormat) {
 | |
|       return make_error<StringError>(
 | |
|           "Target triple cannot be used simultaneously with ELF target format",
 | |
|           ValidationEC);
 | |
|     }
 | |
|     if (ParseTriple) {
 | |
|       IFSTarget TargetFromTriple = parseTriple(Stub.Target.Triple.getValue());
 | |
|       Stub.Target.Arch = TargetFromTriple.Arch;
 | |
|       Stub.Target.BitWidth = TargetFromTriple.BitWidth;
 | |
|       Stub.Target.Endianness = TargetFromTriple.Endianness;
 | |
|     }
 | |
|     return Error::success();
 | |
|   }
 | |
|   if (!Stub.Target.Arch || !Stub.Target.BitWidth || !Stub.Target.Endianness) {
 | |
|     // TODO: unify the error message.
 | |
|     if (!Stub.Target.Arch) {
 | |
|       return make_error<StringError>("Arch is not defined in the text stub",
 | |
|                                      ValidationEC);
 | |
|     }
 | |
|     if (!Stub.Target.BitWidth) {
 | |
|       return make_error<StringError>("BitWidth is not defined in the text stub",
 | |
|                                      ValidationEC);
 | |
|     }
 | |
|     if (!Stub.Target.Endianness) {
 | |
|       return make_error<StringError>(
 | |
|           "Endianness is not defined in the text stub", ValidationEC);
 | |
|     }
 | |
|   }
 | |
|   return Error::success();
 | |
| }
 | |
| 
 | |
| IFSTarget ifs::parseTriple(StringRef TripleStr) {
 | |
|   Triple IFSTriple(TripleStr);
 | |
|   IFSTarget RetTarget;
 | |
|   // TODO: Implement a Triple Arch enum to e_machine map.
 | |
|   switch (IFSTriple.getArch()) {
 | |
|   case Triple::ArchType::aarch64:
 | |
|     RetTarget.Arch = (IFSArch)ELF::EM_AARCH64;
 | |
|     break;
 | |
|   case Triple::ArchType::x86_64:
 | |
|     RetTarget.Arch = (IFSArch)ELF::EM_X86_64;
 | |
|     break;
 | |
|   default:
 | |
|     RetTarget.Arch = (IFSArch)ELF::EM_NONE;
 | |
|   }
 | |
|   RetTarget.Endianness = IFSTriple.isLittleEndian() ? IFSEndiannessType::Little
 | |
|                                                     : IFSEndiannessType::Big;
 | |
|   RetTarget.BitWidth =
 | |
|       IFSTriple.isArch64Bit() ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32;
 | |
|   return RetTarget;
 | |
| }
 | |
| 
 | |
| void ifs::stripIFSTarget(IFSStub &Stub, bool StripTriple, bool StripArch,
 | |
|                          bool StripEndianness, bool StripBitWidth) {
 | |
|   if (StripTriple || StripArch) {
 | |
|     Stub.Target.Arch.reset();
 | |
|     Stub.Target.ArchString.reset();
 | |
|   }
 | |
|   if (StripTriple || StripEndianness) {
 | |
|     Stub.Target.Endianness.reset();
 | |
|   }
 | |
|   if (StripTriple || StripBitWidth) {
 | |
|     Stub.Target.BitWidth.reset();
 | |
|   }
 | |
|   if (StripTriple) {
 | |
|     Stub.Target.Triple.reset();
 | |
|   }
 | |
|   if (!Stub.Target.Arch && !Stub.Target.BitWidth && !Stub.Target.Endianness) {
 | |
|     Stub.Target.ObjectFormat.reset();
 | |
|   }
 | |
| }
 | |
| 
 | |
| Error ifs::filterIFSSyms(IFSStub &Stub, bool StripUndefined,
 | |
|                          const std::vector<std::string> &Exclude) {
 | |
|   std::function<bool(const IFSSymbol &)> Filter = [](const IFSSymbol &) {
 | |
|     return false;
 | |
|   };
 | |
| 
 | |
|   if (StripUndefined) {
 | |
|     Filter = [Filter](const IFSSymbol &Sym) {
 | |
|       return Sym.Undefined || Filter(Sym);
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   for (StringRef Glob : Exclude) {
 | |
|     Expected<llvm::GlobPattern> PatternOrErr = llvm::GlobPattern::create(Glob);
 | |
|     if (!PatternOrErr)
 | |
|       return PatternOrErr.takeError();
 | |
|     Filter = [Pattern = *PatternOrErr, Filter](const IFSSymbol &Sym) {
 | |
|       return Pattern.match(Sym.Name) || Filter(Sym);
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   llvm::erase_if(Stub.Symbols, Filter);
 | |
| 
 | |
|   return Error::success();
 | |
| }
 |