cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
364 lines
12 KiB
C++
364 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/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 "llvm/TargetParser/Triple.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.starts_with("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) {
|
|
uint16_t eMachine =
|
|
ELF::convertArchNameToEMachine(*Stub->Target.ArchString);
|
|
if (eMachine == ELF::EM_NONE)
|
|
return createStringError(
|
|
std::make_error_code(std::errc::invalid_argument),
|
|
"IFS arch '" + *Stub->Target.ArchString + "' is unsupported");
|
|
Stub->Target.Arch = eMachine;
|
|
}
|
|
for (const auto &Item : Stub->Symbols) {
|
|
if (Item.Type == IFSSymbolType::Unknown)
|
|
return createStringError(
|
|
std::make_error_code(std::errc::invalid_argument),
|
|
"IFS symbol type for symbol '" + Item.Name + "' is unsupported");
|
|
}
|
|
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;
|
|
IFSArch TripleArch =
|
|
ELF::convertTripleArchTypeToEMachine(IFSTriple.getArch());
|
|
if (TripleArch != ELF::EM_NONE) {
|
|
RetTarget.Arch = TripleArch;
|
|
}
|
|
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();
|
|
}
|