blob: 8bb01836fccb8a9a82c281d2ad01b83f7a2071f6 [file] [log] [blame]
//===- 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>
#include <optional>
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);
}
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));
}
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, std::optional<IFSArch> OverrideArch,
std::optional<IFSEndiannessType> OverrideEndianness,
std::optional<IFSBitWidthType> OverrideBitWidth,
std::optional<std::string> OverrideTriple) {
std::error_code OverrideEC(1, std::generic_category());
if (OverrideArch) {
if (Stub.Target.Arch && *Stub.Target.Arch != *OverrideArch) {
return make_error<StringError>(
"Supplied Arch conflicts with the text stub", OverrideEC);
}
Stub.Target.Arch = *OverrideArch;
}
if (OverrideEndianness) {
if (Stub.Target.Endianness &&
*Stub.Target.Endianness != *OverrideEndianness) {
return make_error<StringError>(
"Supplied Endianness conflicts with the text stub", OverrideEC);
}
Stub.Target.Endianness = *OverrideEndianness;
}
if (OverrideBitWidth) {
if (Stub.Target.BitWidth && *Stub.Target.BitWidth != *OverrideBitWidth) {
return make_error<StringError>(
"Supplied BitWidth conflicts with the text stub", OverrideEC);
}
Stub.Target.BitWidth = *OverrideBitWidth;
}
if (OverrideTriple) {
if (Stub.Target.Triple && *Stub.Target.Triple != *OverrideTriple) {
return make_error<StringError>(
"Supplied Triple conflicts with the text stub", OverrideEC);
}
Stub.Target.Triple = *OverrideTriple;
}
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);
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();
}