364 lines · cpp
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-exception6//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 23using namespace llvm;24using namespace llvm::ifs;25 26LLVM_YAML_IS_SEQUENCE_VECTOR(IFSSymbol)27 28namespace llvm {29namespace yaml {30 31/// YAML traits for ELFSymbolType.32template <> struct ScalarEnumerationTraits<IFSSymbolType> {33 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 45template <> struct ScalarTraits<IFSEndiannessType> {46 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 60 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 71 static QuotingType mustQuote(StringRef) { return QuotingType::None; }72};73 74template <> struct ScalarTraits<IFSBitWidthType> {75 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 89 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 100 static QuotingType mustQuote(StringRef) { return QuotingType::None; }101};102 103template <> struct MappingTraits<IFSTarget> {104 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.116template <> struct MappingTraits<IFSSymbol> {117 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 we123 // 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.139template <> struct MappingTraits<IFSStub> {140 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.152template <> struct MappingTraits<IFSStubTriple> {153 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 yaml164} // end namespace llvm165 166/// Attempt to determine if a Text stub uses target triple.167bool 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 179Expected<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 213Error 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 else227 YamlOut << *static_cast<IFSStub *>(CopyStub.get());228 return Error::success();229}230 231Error 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 269Error 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 304IFSTarget ifs::parseTriple(StringRef TripleStr) {305 Triple IFSTriple(TripleStr);306 IFSTarget RetTarget;307 IFSArch TripleArch =308 ELF::convertTripleArchTypeToEMachine(IFSTriple.getArch());309 if (TripleArch != ELF::EM_NONE) {310 RetTarget.Arch = TripleArch;311 }312 RetTarget.Endianness = IFSTriple.isLittleEndian() ? IFSEndiannessType::Little313 : IFSEndiannessType::Big;314 RetTarget.BitWidth =315 IFSTriple.isArch64Bit() ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32;316 return RetTarget;317}318 319void ifs::stripIFSTarget(IFSStub &Stub, bool StripTriple, bool StripArch,320 bool StripEndianness, bool StripBitWidth) {321 if (StripTriple || StripArch) {322 Stub.Target.Arch.reset();323 Stub.Target.ArchString.reset();324 }325 if (StripTriple || StripEndianness) {326 Stub.Target.Endianness.reset();327 }328 if (StripTriple || StripBitWidth) {329 Stub.Target.BitWidth.reset();330 }331 if (StripTriple) {332 Stub.Target.Triple.reset();333 }334 if (!Stub.Target.Arch && !Stub.Target.BitWidth && !Stub.Target.Endianness) {335 Stub.Target.ObjectFormat.reset();336 }337}338 339Error ifs::filterIFSSyms(IFSStub &Stub, bool StripUndefined,340 const std::vector<std::string> &Exclude) {341 std::function<bool(const IFSSymbol &)> Filter = [](const IFSSymbol &) {342 return false;343 };344 345 if (StripUndefined) {346 Filter = [Filter](const IFSSymbol &Sym) {347 return Sym.Undefined || Filter(Sym);348 };349 }350 351 for (StringRef Glob : Exclude) {352 Expected<llvm::GlobPattern> PatternOrErr = llvm::GlobPattern::create(Glob);353 if (!PatternOrErr)354 return PatternOrErr.takeError();355 Filter = [Pattern = *PatternOrErr, Filter](const IFSSymbol &Sym) {356 return Pattern.match(Sym.Name) || Filter(Sym);357 };358 }359 360 llvm::erase_if(Stub.Symbols, Filter);361 362 return Error::success();363}364