dsymutil
llvm-dwarfdump
llvm-dwp
+ llvm-elfabi
llvm-exegesis
llvm-extract
llvm-isel-fuzzer
--- /dev/null
+# RUN: yaml2obj %s > %t
+# RUN: llvm-elfabi %t --emit-tbe=- | FileCheck %s
+
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+
+# CHECK: --- !tapi-tbe
+# CHECK-NEXT: TbeVersion: {{[1-9]\d*\.(0|([1-9]\d*))}}
+# CHECK-NEXT: Arch: x86_64
+# CHECK-NEXT: Symbols: {}
+# CHECK-NEXT: ...
--- /dev/null
+# RUN: not llvm-elfabi %s.NotAFileInTestingDir --emit-tbe=%t 2>&1 | FileCheck %s
+
+This file will not be read. An invalid file path is fed to llvm-elfabi.
+
+# CHECK: error: Could not open `{{.*}}.NotAFileInTestingDir`
--- /dev/null
+# RUN: not llvm-elfabi %s --emit-tbe=%t 2>&1 | FileCheck %s
+
+This is just some text that cannot be read by llvm-elfabi.
+
+# CHECK: The file was not recognized as a valid object file
+# CHECK: YAML failed reading as TBE
+# CHECK: No file readers succeeded reading `{{.*}}` (unsupported/malformed file?)
--- /dev/null
+# RUN: yaml2obj %s > %t
+# RUN: llvm-elfabi %t --emit-tbe=- --soname=best.so | FileCheck %s
+
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_AARCH64
+
+# CHECK: --- !tapi-tbe
+# CHECK-NEXT: TbeVersion: {{[1-9]\d*\.(0|([1-9]\d*))}}
+# CHECK-NEXT: SoName: best.so
+# CHECK-NEXT: Arch: AArch64
+# CHECK-NEXT: Symbols: {}
+# CHECK-NEXT: ...
--- /dev/null
+# RUN: llvm-elfabi %s --emit-tbe=- | FileCheck %s
+
+--- !tapi-tbe
+TbeVersion: 1.0
+Arch: AArch64
+Symbols: {}
+...
+
+# As the tbe reader/writer is updated, update this check to ensure --emit-tbe
+# uses the latest tbe writer by default.
+
+# CHECK: --- !tapi-tbe
+# CHECK-NEXT: TbeVersion: 1.0
--- /dev/null
+# RUN: llvm-elfabi %s --emit-tbe=- | FileCheck %s
+
+--- !tapi-tbe
+SoName: somelib.so
+TbeVersion: 1.0
+Arch: x86_64
+Symbols:
+ foo: { Type: Func }
+ bar: { Type: Object, Size: 42 }
+ baz: { Type: Object, Size: 8 }
+ not: { Type: Object, Undefined: true, Size: 128 }
+ nor: { Type: Func, Undefined: true }
+...
+
+# CHECK: --- !tapi-tbe
+# CHECK-NEXT: TbeVersion: {{[1-9]\d*\.(0|([1-9]\d*))}}
+# CHECK-NEXT: SoName: somelib.so
+# CHECK-NEXT: Arch: x86_64
+# CHECK-NEXT: Symbols:
+# CHECK-NEXT: bar: { Type: Object, Size: 42 }
+# CHECK-NEXT: baz: { Type: Object, Size: 8 }
+# CHECK-NEXT: foo: { Type: Func }
+# CHECK-NEXT: nor: { Type: Func, Undefined: true }
+# CHECK-NEXT: not: { Type: Object, Size: 128, Undefined: true }
+# CHECK-NEXT: ...
llvm-dis
llvm-dwarfdump
llvm-dwp
+ llvm-elfabi
llvm-exegesis
llvm-extract
llvm-jitlistener
--- /dev/null
+set(LLVM_LINK_COMPONENTS
+ Object
+ Support
+ TextAPI
+ )
+
+add_llvm_tool(llvm-elfabi
+ ELFObjHandler.cpp
+ ErrorCollector.cpp
+ llvm-elfabi.cpp
+ )
--- /dev/null
+//===- ELFObjHandler.cpp --------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+
+#include "ELFObjHandler.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/TextAPI/ELF/ELFStub.h"
+
+using llvm::MemoryBufferRef;
+using llvm::object::ELFObjectFile;
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::elfabi;
+using namespace llvm::ELF;
+
+namespace llvm {
+namespace elfabi {
+
+/// Returns a new ELFStub with all members populated from an ELFObjectFile.
+/// @param ElfObj Source ELFObjectFile.
+template <class ELFT>
+Expected<std::unique_ptr<ELFStub>>
+buildStub(const ELFObjectFile<ELFT> &ElfObj) {
+ std::unique_ptr<ELFStub> DestStub = make_unique<ELFStub>();
+ const ELFFile<ELFT> *ElfFile = ElfObj.getELFFile();
+
+ DestStub->Arch = ElfFile->getHeader()->e_machine;
+
+ // TODO: Populate SoName from .dynamic entries and linked string table.
+ // TODO: Populate NeededLibs from .dynamic entries and linked string table.
+ // TODO: Populate Symbols from .dynsym table and linked string table.
+
+ return std::move(DestStub);
+}
+
+Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) {
+ Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buf);
+ if (!BinOrErr) {
+ return BinOrErr.takeError();
+ }
+
+ Binary *Bin = BinOrErr->get();
+ if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
+ return buildStub(*Obj);
+ } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
+ return buildStub(*Obj);
+ } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
+ return buildStub(*Obj);
+ } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
+ return buildStub(*Obj);
+ }
+
+ return createStringError(errc::not_supported, "Unsupported binary format");
+}
+
+} // end namespace elfabi
+} // end namespace llvm
--- /dev/null
+//===- ELFObjHandler.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+///
+/// This supports reading and writing of elf dynamic shared objects.
+///
+//===-----------------------------------------------------------------------===/
+
+#ifndef LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H
+#define LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H
+
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/TextAPI/ELF/ELFStub.h"
+
+namespace llvm {
+
+class MemoryBuffer;
+
+namespace elfabi {
+
+/// Attempt to read a binary ELF file from a MemoryBuffer.
+Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf);
+
+} // end namespace elfabi
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H
--- /dev/null
+//===- ErrorCollector.cpp -------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+
+#include "ErrorCollector.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/WithColor.h"
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::elfabi;
+
+void ErrorCollector::escalateToFatal() {
+ ErrorsAreFatal = true;
+}
+
+void ErrorCollector::addError(Error &&Err, StringRef Tag) {
+ if (Err) {
+ Errors.push_back(std::move(Err));
+ Tags.push_back(Tag.str());
+ }
+}
+
+Error ErrorCollector::makeError() {
+ // TODO: Make this return something (an AggregateError?) that gives more
+ // individual control over each error and which might be of interest.
+ Error JoinedErrors = Error::success();
+ for (Error &E : Errors) {
+ JoinedErrors = joinErrors(std::move(JoinedErrors), std::move(E));
+ }
+ Errors.clear();
+ Tags.clear();
+ return JoinedErrors;
+}
+
+void ErrorCollector::log(raw_ostream &OS) {
+ OS << "Encountered multiple errors:\n";
+ for (size_t i = 0; i < Errors.size(); ++i) {
+ WithColor::error(OS) << "(" << Tags[i] << ") " << Errors[i];
+ if (i != Errors.size() - 1)
+ OS << "\n";
+ }
+}
+
+bool ErrorCollector::allErrorsHandled() const {
+ return Errors.empty();
+}
+
+ErrorCollector::~ErrorCollector() {
+ if (ErrorsAreFatal && !allErrorsHandled())
+ fatalUnhandledError();
+
+ for (Error &E : Errors) {
+ consumeError(std::move(E));
+ }
+}
+
+LLVM_ATTRIBUTE_NORETURN void ErrorCollector::fatalUnhandledError() {
+ errs() << "Program aborted due to unhandled Error(s):\n";
+ log(errs());
+ errs() << "\n";
+ abort();
+}
--- /dev/null
+//===- ErrorCollector.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+///
+/// This class collects errors that should be reported or ignored in aggregate.
+///
+/// Like llvm::Error, an ErrorCollector cannot be copied. Unlike llvm::Error,
+/// an ErrorCollector may be destroyed if it was originally constructed to treat
+/// errors as non-fatal. In this case, all Errors are consumed upon destruction.
+/// An ErrorCollector may be initially constructed (or escalated) such that
+/// errors are treated as fatal. This causes a crash if an attempt is made to
+/// delete the ErrorCollector when some Errors have not been retrieved via
+/// makeError().
+///
+//===-----------------------------------------------------------------------===/
+
+#ifndef LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H
+#define LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H
+
+#include "llvm/Support/Error.h"
+#include <vector>
+
+namespace llvm {
+namespace elfabi {
+
+class ErrorCollector {
+public:
+ /// Upon destruction, an ErrorCollector will crash if UseFatalErrors=true and
+ /// there are remaining errors that haven't been fetched by makeError().
+ ErrorCollector(bool UseFatalErrors = true) : ErrorsAreFatal(UseFatalErrors) {}
+ // Don't allow copying.
+ ErrorCollector(const ErrorCollector &Stub) = delete;
+ ErrorCollector &operator=(const ErrorCollector &Other) = delete;
+ ~ErrorCollector();
+
+ // TODO: Add move constructor and operator= when a testable situation arises.
+
+ /// Returns a single error that contains messages for all stored Errors.
+ Error makeError();
+
+ /// Adds an error with a descriptive tag that helps with identification.
+ /// If the error is an Error::success(), it is checked and discarded.
+ void addError(Error &&E, StringRef Tag);
+
+ /// This ensures an ErrorCollector will treat unhandled errors as fatal.
+ /// This function should be called if errors that usually can be ignored
+ /// are suddenly of concern (i.e. attempt multiple things that return Error,
+ /// but only care about the Errors if no attempt succeeds).
+ void escalateToFatal();
+
+private:
+ /// Logs all errors to a raw_ostream.
+ void log(raw_ostream &OS);
+
+ /// Returns true if all errors have been retrieved through makeError(), or
+ /// false if errors have been added since the last makeError() call.
+ bool allErrorsHandled() const;
+
+ /// Dump output and crash.
+ LLVM_ATTRIBUTE_NORETURN void fatalUnhandledError();
+
+ bool ErrorsAreFatal;
+ std::vector<Error> Errors;
+ std::vector<std::string> Tags;
+};
+
+} // end namespace elfabi
+} // end namespace llvm
+
+#endif // LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H
--- /dev/null
+;===- ./tools/llvm-elfabi/LLVMBuild.txt ------------------------*- Conf -*--===;
+;
+; The LLVM Compiler Infrastructure
+;
+; This file is distributed under the University of Illinois Open Source
+; License. See LICENSE.TXT for details.
+;
+;===------------------------------------------------------------------------===;
+;
+; This is an LLVMBuild description file for the components in this subdirectory.
+;
+; For more information on the LLVMBuild system, please see:
+;
+; http://llvm.org/docs/LLVMBuild.html
+;
+;===------------------------------------------------------------------------===;
+
+[component_0]
+type = Tool
+name = llvm-elfabi
+parent = Tools
+required_libraries = Object Support TextAPI
--- /dev/null
+//===- llvm-elfabi.cpp ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-----------------------------------------------------------------------===/
+
+#include "ELFObjHandler.h"
+#include "ErrorCollector.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/TextAPI/ELF/TBEHandler.h"
+#include <string>
+
+using namespace llvm;
+using namespace llvm::elfabi;
+
+// Command line flags:
+cl::opt<std::string> InputFilePath(cl::Positional, cl::desc("input"),
+ cl::Required);
+cl::opt<std::string>
+ EmitTBE("emit-tbe",
+ cl::desc("Emit a text-based ELF stub (.tbe) from the input file"),
+ cl::value_desc("path"));
+cl::opt<std::string> SOName(
+ "soname",
+ cl::desc("Manually set the DT_SONAME entry of any emitted files"),
+ cl::value_desc("name"));
+
+/// writeTBE() writes a Text-Based ELF stub to a file using the latest version
+/// of the YAML parser.
+static Error writeTBE(StringRef FilePath, ELFStub &Stub) {
+ std::error_code SysErr;
+
+ // Open file for writing.
+ raw_fd_ostream Out(FilePath, SysErr);
+ if (SysErr)
+ return createStringError(SysErr, "Couldn't open `%s` for writing",
+ FilePath.data());
+ // Write file.
+ Error YAMLErr = writeTBEToOutputStream(Out, Stub);
+ if (YAMLErr)
+ return YAMLErr;
+
+ return Error::success();
+}
+
+/// readInputFile populates an ELFStub by attempting to read the
+/// input file using both the TBE and binary ELF parsers.
+static Expected<std::unique_ptr<ELFStub>> readInputFile(StringRef FilePath) {
+ // Read in file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError =
+ MemoryBuffer::getFile(FilePath);
+ if (!BufOrError) {
+ return createStringError(BufOrError.getError(), "Could not open `%s`",
+ FilePath.data());
+ }
+
+ std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError);
+ ErrorCollector EC(/*UseFatalErrors=*/false);
+
+ // First try to read as a binary (fails fast if not binary).
+ Expected<std::unique_ptr<ELFStub>> StubFromELF =
+ readELFFile(FileReadBuffer->getMemBufferRef());
+ if (StubFromELF) {
+ return std::move(*StubFromELF);
+ }
+ EC.addError(StubFromELF.takeError(), "BinaryRead");
+
+ // Fall back to reading as a tbe.
+ Expected<std::unique_ptr<ELFStub>> StubFromTBE =
+ readTBEFromBuffer(FileReadBuffer->getBuffer());
+ if (StubFromTBE) {
+ return std::move(*StubFromTBE);
+ }
+ EC.addError(StubFromTBE.takeError(), "YamlParse");
+
+ // If both readers fail, build a new error that includes all information.
+ EC.addError(createStringError(errc::not_supported,
+ "No file readers succeeded reading `%s` "
+ "(unsupported/malformed file?)",
+ FilePath.data()),
+ "ReadInputFile");
+ EC.escalateToFatal();
+ return EC.makeError();
+}
+
+int main(int argc, char *argv[]) {
+ // Parse arguments.
+ cl::ParseCommandLineOptions(argc, argv);
+
+ Expected<std::unique_ptr<ELFStub>> StubOrErr = readInputFile(InputFilePath);
+ if (!StubOrErr) {
+ Error ReadError = StubOrErr.takeError();
+ WithColor::error() << ReadError << "\n";
+ exit(1);
+ }
+
+ std::unique_ptr<ELFStub> TargetStub = std::move(StubOrErr.get());
+
+ // Write out .tbe file.
+ if (EmitTBE.getNumOccurrences() == 1) {
+ TargetStub->TbeVersion = TBEVersionCurrent;
+ if (SOName.getNumOccurrences() == 1) {
+ TargetStub->SoName = SOName;
+ }
+ Error TBEWriteError = writeTBE(EmitTBE, *TargetStub);
+ if (TBEWriteError) {
+ WithColor::error() << TBEWriteError << "\n";
+ exit(1);
+ }
+ }
+}