llvm-project
635 строк · 22.4 Кб
1//===- llvm-ifs.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-exception
6//
7//===-----------------------------------------------------------------------===/
8
9#include "ErrorCollector.h"10#include "llvm/ADT/StringRef.h"11#include "llvm/ADT/StringSwitch.h"12#include "llvm/BinaryFormat/ELF.h"13#include "llvm/InterfaceStub/ELFObjHandler.h"14#include "llvm/InterfaceStub/IFSHandler.h"15#include "llvm/InterfaceStub/IFSStub.h"16#include "llvm/ObjectYAML/yaml2obj.h"17#include "llvm/Option/Arg.h"18#include "llvm/Option/ArgList.h"19#include "llvm/Option/Option.h"20#include "llvm/Support/CommandLine.h"21#include "llvm/Support/Debug.h"22#include "llvm/Support/Errc.h"23#include "llvm/Support/Error.h"24#include "llvm/Support/FileOutputBuffer.h"25#include "llvm/Support/LLVMDriver.h"26#include "llvm/Support/MemoryBuffer.h"27#include "llvm/Support/Path.h"28#include "llvm/Support/VersionTuple.h"29#include "llvm/Support/WithColor.h"30#include "llvm/Support/YAMLTraits.h"31#include "llvm/Support/raw_ostream.h"32#include "llvm/TargetParser/Triple.h"33#include "llvm/TextAPI/InterfaceFile.h"34#include "llvm/TextAPI/TextAPIReader.h"35#include "llvm/TextAPI/TextAPIWriter.h"36#include <optional>37#include <set>38#include <string>39#include <vector>40
41using namespace llvm;42using namespace llvm::yaml;43using namespace llvm::MachO;44using namespace llvm::ifs;45
46#define DEBUG_TYPE "llvm-ifs"47
48namespace {49const VersionTuple IfsVersionCurrent(3, 0);50
51enum class FileFormat { IFS, ELF, TBD };52} // end anonymous namespace53
54using namespace llvm::opt;55enum ID {56OPT_INVALID = 0, // This is not an option ID.57#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),58#include "Opts.inc"59#undef OPTION60};61
62#define PREFIX(NAME, VALUE) \63static constexpr StringLiteral NAME##_init[] = VALUE; \64static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \65std::size(NAME##_init) - 1);66#include "Opts.inc"67#undef PREFIX68
69static constexpr opt::OptTable::Info InfoTable[] = {70#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),71#include "Opts.inc"72#undef OPTION73};74
75class IFSOptTable : public opt::GenericOptTable {76public:77IFSOptTable() : opt::GenericOptTable(InfoTable) {78setGroupedShortOptions(true);79}80};81
82struct DriverConfig {83std::vector<std::string> InputFilePaths;84
85std::optional<FileFormat> InputFormat;86std::optional<FileFormat> OutputFormat;87
88std::optional<std::string> HintIfsTarget;89std::optional<std::string> OptTargetTriple;90std::optional<IFSArch> OverrideArch;91std::optional<IFSBitWidthType> OverrideBitWidth;92std::optional<IFSEndiannessType> OverrideEndianness;93
94bool StripIfsArch = false;95bool StripIfsBitwidth = false;96bool StripIfsEndianness = false;97bool StripIfsTarget = false;98bool StripNeeded = false;99bool StripSize = false;100bool StripUndefined = false;101
102std::vector<std::string> Exclude;103
104std::optional<std::string> SoName;105
106std::optional<std::string> Output;107std::optional<std::string> OutputElf;108std::optional<std::string> OutputIfs;109std::optional<std::string> OutputTbd;110
111bool WriteIfChanged = false;112};113
114static std::string getTypeName(IFSSymbolType Type) {115switch (Type) {116case IFSSymbolType::NoType:117return "NoType";118case IFSSymbolType::Func:119return "Func";120case IFSSymbolType::Object:121return "Object";122case IFSSymbolType::TLS:123return "TLS";124case IFSSymbolType::Unknown:125return "Unknown";126}127llvm_unreachable("Unexpected ifs symbol type.");128}
129
130static Expected<std::unique_ptr<IFSStub>>131readInputFile(std::optional<FileFormat> &InputFormat, StringRef FilePath) {132// Read in file.133ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =134MemoryBuffer::getFileOrSTDIN(FilePath, /*IsText=*/true);135if (!BufOrError)136return createStringError(BufOrError.getError(), "Could not open `%s`",137FilePath.data());138
139std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError);140ErrorCollector EC(/*UseFatalErrors=*/false);141
142// First try to read as a binary (fails fast if not binary).143if (!InputFormat || *InputFormat == FileFormat::ELF) {144Expected<std::unique_ptr<IFSStub>> StubFromELF =145readELFFile(FileReadBuffer->getMemBufferRef());146if (StubFromELF) {147InputFormat = FileFormat::ELF;148(*StubFromELF)->IfsVersion = IfsVersionCurrent;149return std::move(*StubFromELF);150}151EC.addError(StubFromELF.takeError(), "BinaryRead");152}153
154// Fall back to reading as a ifs.155if (!InputFormat || *InputFormat == FileFormat::IFS) {156Expected<std::unique_ptr<IFSStub>> StubFromIFS =157readIFSFromBuffer(FileReadBuffer->getBuffer());158if (StubFromIFS) {159InputFormat = FileFormat::IFS;160if ((*StubFromIFS)->IfsVersion > IfsVersionCurrent)161EC.addError(162createStringError(errc::not_supported,163"IFS version " +164(*StubFromIFS)->IfsVersion.getAsString() +165" is unsupported."),166"ReadInputFile");167else168return std::move(*StubFromIFS);169} else {170EC.addError(StubFromIFS.takeError(), "YamlParse");171}172}173
174// If both readers fail, build a new error that includes all information.175EC.addError(createStringError(errc::not_supported,176"No file readers succeeded reading `%s` "177"(unsupported/malformed file?)",178FilePath.data()),179"ReadInputFile");180EC.escalateToFatal();181return EC.makeError();182}
183
184static int writeTbdStub(const Triple &T, const std::vector<IFSSymbol> &Symbols,185const StringRef Format, raw_ostream &Out) {186
187auto PlatformTypeOrError =188[](const llvm::Triple &T) -> llvm::Expected<llvm::MachO::PlatformType> {189if (T.isMacOSX())190return llvm::MachO::PLATFORM_MACOS;191if (T.isTvOS())192return llvm::MachO::PLATFORM_TVOS;193if (T.isWatchOS())194return llvm::MachO::PLATFORM_WATCHOS;195// Note: put isiOS last because tvOS and watchOS are also iOS according196// to the Triple.197if (T.isiOS())198return llvm::MachO::PLATFORM_IOS;199
200return createStringError(errc::not_supported, "Invalid Platform.\n");201}(T);202
203if (!PlatformTypeOrError)204return -1;205
206PlatformType Plat = PlatformTypeOrError.get();207TargetList Targets({Target(llvm::MachO::mapToArchitecture(T), Plat)});208
209InterfaceFile File;210File.setFileType(FileType::TBD_V3); // Only supporting v3 for now.211File.addTargets(Targets);212
213for (const auto &Symbol : Symbols) {214auto Name = Symbol.Name;215auto Kind = EncodeKind::GlobalSymbol;216switch (Symbol.Type) {217default:218case IFSSymbolType::NoType:219Kind = EncodeKind::GlobalSymbol;220break;221case IFSSymbolType::Object:222Kind = EncodeKind::GlobalSymbol;223break;224case IFSSymbolType::Func:225Kind = EncodeKind::GlobalSymbol;226break;227}228if (Symbol.Weak)229File.addSymbol(Kind, Name, Targets, SymbolFlags::WeakDefined);230else231File.addSymbol(Kind, Name, Targets);232}233
234SmallString<4096> Buffer;235raw_svector_ostream OS(Buffer);236if (Error Result = TextAPIWriter::writeToStream(OS, File))237return -1;238Out << OS.str();239return 0;240}
241
242static void fatalError(Error Err) {243WithColor::defaultErrorHandler(std::move(Err));244exit(1);245}
246
247static void fatalError(Twine T) {248WithColor::error() << T.str() << '\n';249exit(1);250}
251
252/// writeIFS() writes a Text-Based ELF stub to a file using the latest version
253/// of the YAML parser.
254static Error writeIFS(StringRef FilePath, IFSStub &Stub, bool WriteIfChanged) {255// Write IFS to memory first.256std::string IFSStr;257raw_string_ostream OutStr(IFSStr);258Error YAMLErr = writeIFSToOutputStream(OutStr, Stub);259if (YAMLErr)260return YAMLErr;261OutStr.flush();262
263if (WriteIfChanged) {264if (ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =265MemoryBuffer::getFile(FilePath)) {266// Compare IFS output with the existing IFS file. If unchanged, avoid267// changing the file.268if ((*BufOrError)->getBuffer() == IFSStr)269return Error::success();270}271}272// Open IFS file for writing.273std::error_code SysErr;274raw_fd_ostream Out(FilePath, SysErr);275if (SysErr)276return createStringError(SysErr, "Couldn't open `%s` for writing",277FilePath.data());278Out << IFSStr;279return Error::success();280}
281
282static DriverConfig parseArgs(int argc, char *const *argv) {283BumpPtrAllocator A;284StringSaver Saver(A);285IFSOptTable Tbl;286StringRef ToolName = argv[0];287llvm::opt::InputArgList Args = Tbl.parseArgs(288argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) { fatalError(Msg); });289if (Args.hasArg(OPT_help)) {290Tbl.printHelp(llvm::outs(),291(Twine(ToolName) + " <input_file> <output_file> [options]")292.str()293.c_str(),294"shared object stubbing tool");295std::exit(0);296}297if (Args.hasArg(OPT_version)) {298llvm::outs() << ToolName << '\n';299cl::PrintVersionMessage();300std::exit(0);301}302
303DriverConfig Config;304for (const opt::Arg *A : Args.filtered(OPT_INPUT))305Config.InputFilePaths.push_back(A->getValue());306if (const opt::Arg *A = Args.getLastArg(OPT_input_format_EQ)) {307Config.InputFormat = StringSwitch<std::optional<FileFormat>>(A->getValue())308.Case("IFS", FileFormat::IFS)309.Case("ELF", FileFormat::ELF)310.Default(std::nullopt);311if (!Config.InputFormat)312fatalError(Twine("invalid argument '") + A->getValue());313}314
315auto OptionNotFound = [ToolName](StringRef FlagName, StringRef OptionName) {316fatalError(Twine(ToolName) + ": for the " + FlagName +317" option: Cannot find option named '" + OptionName + "'!");318};319if (const opt::Arg *A = Args.getLastArg(OPT_output_format_EQ)) {320Config.OutputFormat = StringSwitch<std::optional<FileFormat>>(A->getValue())321.Case("IFS", FileFormat::IFS)322.Case("ELF", FileFormat::ELF)323.Case("TBD", FileFormat::TBD)324.Default(std::nullopt);325if (!Config.OutputFormat)326OptionNotFound("--output-format", A->getValue());327}328if (const opt::Arg *A = Args.getLastArg(OPT_arch_EQ)) {329uint16_t eMachine = ELF::convertArchNameToEMachine(A->getValue());330if (eMachine == ELF::EM_NONE) {331fatalError(Twine("unknown arch '") + A->getValue() + "'");332}333Config.OverrideArch = eMachine;334}335if (const opt::Arg *A = Args.getLastArg(OPT_bitwidth_EQ)) {336size_t Width;337llvm::StringRef S(A->getValue());338if (!S.getAsInteger<size_t>(10, Width) || Width == 64 || Width == 32)339Config.OverrideBitWidth =340Width == 64 ? IFSBitWidthType::IFS64 : IFSBitWidthType::IFS32;341else342OptionNotFound("--bitwidth", A->getValue());343}344if (const opt::Arg *A = Args.getLastArg(OPT_endianness_EQ)) {345Config.OverrideEndianness =346StringSwitch<std::optional<IFSEndiannessType>>(A->getValue())347.Case("little", IFSEndiannessType::Little)348.Case("big", IFSEndiannessType::Big)349.Default(std::nullopt);350if (!Config.OverrideEndianness)351OptionNotFound("--endianness", A->getValue());352}353if (const opt::Arg *A = Args.getLastArg(OPT_target_EQ))354Config.OptTargetTriple = A->getValue();355if (const opt::Arg *A = Args.getLastArg(OPT_hint_ifs_target_EQ))356Config.HintIfsTarget = A->getValue();357
358Config.StripIfsArch = Args.hasArg(OPT_strip_ifs_arch);359Config.StripIfsBitwidth = Args.hasArg(OPT_strip_ifs_bitwidth);360Config.StripIfsEndianness = Args.hasArg(OPT_strip_ifs_endianness);361Config.StripIfsTarget = Args.hasArg(OPT_strip_ifs_target);362Config.StripUndefined = Args.hasArg(OPT_strip_undefined);363Config.StripNeeded = Args.hasArg(OPT_strip_needed);364Config.StripSize = Args.hasArg(OPT_strip_size);365
366for (const opt::Arg *A : Args.filtered(OPT_exclude_EQ))367Config.Exclude.push_back(A->getValue());368if (const opt::Arg *A = Args.getLastArg(OPT_soname_EQ))369Config.SoName = A->getValue();370if (const opt::Arg *A = Args.getLastArg(OPT_output_EQ))371Config.Output = A->getValue();372if (const opt::Arg *A = Args.getLastArg(OPT_output_elf_EQ))373Config.OutputElf = A->getValue();374if (const opt::Arg *A = Args.getLastArg(OPT_output_ifs_EQ))375Config.OutputIfs = A->getValue();376if (const opt::Arg *A = Args.getLastArg(OPT_output_tbd_EQ))377Config.OutputTbd = A->getValue();378Config.WriteIfChanged = Args.hasArg(OPT_write_if_changed);379return Config;380}
381
382int llvm_ifs_main(int argc, char **argv, const llvm::ToolContext &) {383DriverConfig Config = parseArgs(argc, argv);384
385if (Config.InputFilePaths.empty())386Config.InputFilePaths.push_back("-");387
388// If input files are more than one, they can only be IFS files.389if (Config.InputFilePaths.size() > 1)390Config.InputFormat = FileFormat::IFS;391
392// Attempt to merge input.393IFSStub Stub;394std::map<std::string, IFSSymbol> SymbolMap;395std::string PreviousInputFilePath;396for (const std::string &InputFilePath : Config.InputFilePaths) {397Expected<std::unique_ptr<IFSStub>> StubOrErr =398readInputFile(Config.InputFormat, InputFilePath);399if (!StubOrErr)400fatalError(StubOrErr.takeError());401
402std::unique_ptr<IFSStub> TargetStub = std::move(StubOrErr.get());403if (PreviousInputFilePath.empty()) {404Stub.IfsVersion = TargetStub->IfsVersion;405Stub.Target = TargetStub->Target;406Stub.SoName = TargetStub->SoName;407Stub.NeededLibs = TargetStub->NeededLibs;408} else {409if (Stub.IfsVersion != TargetStub->IfsVersion) {410if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {411WithColor::error()412<< "Interface Stub: IfsVersion Mismatch."413<< "\nFilenames: " << PreviousInputFilePath << " "414<< InputFilePath << "\nIfsVersion Values: " << Stub.IfsVersion415<< " " << TargetStub->IfsVersion << "\n";416return -1;417}418if (TargetStub->IfsVersion > Stub.IfsVersion)419Stub.IfsVersion = TargetStub->IfsVersion;420}421if (Stub.Target != TargetStub->Target && !TargetStub->Target.empty()) {422WithColor::error() << "Interface Stub: Target Mismatch."423<< "\nFilenames: " << PreviousInputFilePath << " "424<< InputFilePath;425return -1;426}427if (Stub.SoName != TargetStub->SoName) {428WithColor::error() << "Interface Stub: SoName Mismatch."429<< "\nFilenames: " << PreviousInputFilePath << " "430<< InputFilePath431<< "\nSoName Values: " << Stub.SoName << " "432<< TargetStub->SoName << "\n";433return -1;434}435if (Stub.NeededLibs != TargetStub->NeededLibs) {436WithColor::error() << "Interface Stub: NeededLibs Mismatch."437<< "\nFilenames: " << PreviousInputFilePath << " "438<< InputFilePath << "\n";439return -1;440}441}442
443for (auto Symbol : TargetStub->Symbols) {444auto SI = SymbolMap.find(Symbol.Name);445if (SI == SymbolMap.end()) {446SymbolMap.insert(447std::pair<std::string, IFSSymbol>(Symbol.Name, Symbol));448continue;449}450
451assert(Symbol.Name == SI->second.Name && "Symbol Names Must Match.");452
453// Check conflicts:454if (Symbol.Type != SI->second.Type) {455WithColor::error() << "Interface Stub: Type Mismatch for "456<< Symbol.Name << ".\nFilename: " << InputFilePath457<< "\nType Values: " << getTypeName(SI->second.Type)458<< " " << getTypeName(Symbol.Type) << "\n";459
460return -1;461}462if (Symbol.Size != SI->second.Size) {463WithColor::error() << "Interface Stub: Size Mismatch for "464<< Symbol.Name << ".\nFilename: " << InputFilePath465<< "\nSize Values: " << SI->second.Size << " "466<< Symbol.Size << "\n";467
468return -1;469}470if (Symbol.Weak != SI->second.Weak) {471Symbol.Weak = false;472continue;473}474// TODO: Not checking Warning. Will be dropped.475}476
477PreviousInputFilePath = InputFilePath;478}479
480if (Stub.IfsVersion != IfsVersionCurrent)481if (Stub.IfsVersion.getMajor() != IfsVersionCurrent.getMajor()) {482WithColor::error() << "Interface Stub: Bad IfsVersion: "483<< Stub.IfsVersion << ", llvm-ifs supported version: "484<< IfsVersionCurrent << ".\n";485return -1;486}487
488for (auto &Entry : SymbolMap)489Stub.Symbols.push_back(Entry.second);490
491// Change SoName before emitting stubs.492if (Config.SoName)493Stub.SoName = *Config.SoName;494
495Error OverrideError =496overrideIFSTarget(Stub, Config.OverrideArch, Config.OverrideEndianness,497Config.OverrideBitWidth, Config.OptTargetTriple);498if (OverrideError)499fatalError(std::move(OverrideError));500
501if (Config.StripNeeded)502Stub.NeededLibs.clear();503
504if (Error E = filterIFSSyms(Stub, Config.StripUndefined, Config.Exclude))505fatalError(std::move(E));506
507if (Config.StripSize)508for (IFSSymbol &Sym : Stub.Symbols)509Sym.Size.reset();510
511if (!Config.OutputElf && !Config.OutputIfs && !Config.OutputTbd) {512if (!Config.OutputFormat) {513WithColor::error() << "at least one output should be specified.";514return -1;515}516} else if (Config.OutputFormat) {517WithColor::error() << "'--output-format' cannot be used with "518"'--output-{FILE_FORMAT}' options at the same time";519return -1;520}521if (Config.OutputFormat) {522// TODO: Remove OutputFormat flag in the next revision.523WithColor::warning() << "--output-format option is deprecated, please use "524"--output-{FILE_FORMAT} options instead\n";525switch (*Config.OutputFormat) {526case FileFormat::TBD: {527std::error_code SysErr;528raw_fd_ostream Out(*Config.Output, SysErr);529if (SysErr) {530WithColor::error() << "Couldn't open " << *Config.Output531<< " for writing.\n";532return -1;533}534if (!Stub.Target.Triple) {535WithColor::error()536<< "Triple should be defined when output format is TBD";537return -1;538}539return writeTbdStub(llvm::Triple(*Stub.Target.Triple), Stub.Symbols,540"TBD", Out);541}542case FileFormat::IFS: {543Stub.IfsVersion = IfsVersionCurrent;544if (*Config.InputFormat == FileFormat::ELF && Config.HintIfsTarget) {545std::error_code HintEC(1, std::generic_category());546IFSTarget HintTarget = parseTriple(*Config.HintIfsTarget);547if (*Stub.Target.Arch != *HintTarget.Arch)548fatalError(make_error<StringError>(549"Triple hint does not match the actual architecture", HintEC));550if (*Stub.Target.Endianness != *HintTarget.Endianness)551fatalError(make_error<StringError>(552"Triple hint does not match the actual endianness", HintEC));553if (*Stub.Target.BitWidth != *HintTarget.BitWidth)554fatalError(make_error<StringError>(555"Triple hint does not match the actual bit width", HintEC));556
557stripIFSTarget(Stub, true, false, false, false);558Stub.Target.Triple = *Config.HintIfsTarget;559} else {560stripIFSTarget(Stub, Config.StripIfsTarget, Config.StripIfsArch,561Config.StripIfsEndianness, Config.StripIfsBitwidth);562}563Error IFSWriteError =564writeIFS(*Config.Output, Stub, Config.WriteIfChanged);565if (IFSWriteError)566fatalError(std::move(IFSWriteError));567break;568}569case FileFormat::ELF: {570Error TargetError = validateIFSTarget(Stub, true);571if (TargetError)572fatalError(std::move(TargetError));573Error BinaryWriteError =574writeBinaryStub(*Config.Output, Stub, Config.WriteIfChanged);575if (BinaryWriteError)576fatalError(std::move(BinaryWriteError));577break;578}579}580} else {581// Check if output path for individual format.582if (Config.OutputElf) {583Error TargetError = validateIFSTarget(Stub, true);584if (TargetError)585fatalError(std::move(TargetError));586Error BinaryWriteError =587writeBinaryStub(*Config.OutputElf, Stub, Config.WriteIfChanged);588if (BinaryWriteError)589fatalError(std::move(BinaryWriteError));590}591if (Config.OutputIfs) {592Stub.IfsVersion = IfsVersionCurrent;593if (*Config.InputFormat == FileFormat::ELF && Config.HintIfsTarget) {594std::error_code HintEC(1, std::generic_category());595IFSTarget HintTarget = parseTriple(*Config.HintIfsTarget);596if (*Stub.Target.Arch != *HintTarget.Arch)597fatalError(make_error<StringError>(598"Triple hint does not match the actual architecture", HintEC));599if (*Stub.Target.Endianness != *HintTarget.Endianness)600fatalError(make_error<StringError>(601"Triple hint does not match the actual endianness", HintEC));602if (*Stub.Target.BitWidth != *HintTarget.BitWidth)603fatalError(make_error<StringError>(604"Triple hint does not match the actual bit width", HintEC));605
606stripIFSTarget(Stub, true, false, false, false);607Stub.Target.Triple = *Config.HintIfsTarget;608} else {609stripIFSTarget(Stub, Config.StripIfsTarget, Config.StripIfsArch,610Config.StripIfsEndianness, Config.StripIfsBitwidth);611}612Error IFSWriteError =613writeIFS(*Config.OutputIfs, Stub, Config.WriteIfChanged);614if (IFSWriteError)615fatalError(std::move(IFSWriteError));616}617if (Config.OutputTbd) {618std::error_code SysErr;619raw_fd_ostream Out(*Config.OutputTbd, SysErr);620if (SysErr) {621WithColor::error() << "Couldn't open " << *Config.OutputTbd622<< " for writing.\n";623return -1;624}625if (!Stub.Target.Triple) {626WithColor::error()627<< "Triple should be defined when output format is TBD";628return -1;629}630return writeTbdStub(llvm::Triple(*Stub.Target.Triple), Stub.Symbols,631"TBD", Out);632}633}634return 0;635}
636