forked from OSchip/llvm-project
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);
|
|
}
|
|
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);
|
|
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();
|
|
}
|