Index: llvm/include/llvm/TAPI/ELFStubFile.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/ELFStubFile.h @@ -0,0 +1,134 @@ +//===- ELFStubFile.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ +// +// This file defines an internal representation of an ELF stub as well as an +// interface for interacting with ELF files. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TAPI_ELFSTUBFILE_H +#define LLVM_TAPI_ELFSTUBFILE_H + +#include "llvm/Object/ELF.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { +namespace tapi { + +typedef uint16_t ELFArch; + +enum FileType : unsigned { + Invalid = 0u, + + // bits 0 - 10 reserved for XCode + + // ELF dynamic shared object + ELF_32LE = 1u << 11, + ELF_32BE = 1u << 12, + ELF_64LE = 1u << 13, + ELF_64BE = 1u << 14, + // Text-based ELF stub file (.tbe) version 1.0 + TBE_V1 = 1u << 15, + All = ~0u, +}; + +enum ELFSymbolType { + NoType = ELF::STT_NOTYPE, + Object = ELF::STT_OBJECT, + Func = ELF::STT_FUNC, + TLS = ELF::STT_TLS, + + // Type information is a nibble, so 16 is safely out of range. + Unknown = 16, +}; + +struct ELFSymbol { + std::string StName; + unsigned int StSize; + ELFSymbolType StType; + bool StUndefined; + Optional StWarning; +}; + +struct ELFStub { + std::vector Symbols; + ELFArch Arch; + std::string SoName; +}; + +// A cumulative representation of ELF stubs. +// Both textual and binary stubs will read into and write from this object. +class ELFStubFile { +public: + explicit ELFStubFile(FileType Type) { + this->Type = Type; + } + explicit ELFStubFile(FileType Type, ELFStub &SourceStub) { + this->Type = Type; + + Stub.Symbols.clear(); + Stub.Arch = SourceStub.Arch; + Stub.SoName = SourceStub.SoName; + for (auto Elem : SourceStub.Symbols) { + Stub.Symbols.push_back(Elem); + } + std::sort(Stub.Symbols.begin(), + Stub.Symbols.end(), + symbolCompareTo); + } + + // Returns all FileTypes this file can be converted among. + uint32_t getCompatibleTypes() const { return SUPPORTED_FILE_TYPES; } + + // Returns original FileType of this file. + enum FileType getFileType() const { return Type; } + + // Returns whether or not type is a compatible type. + bool supportsType(FileType OtherType) const { + if (OtherType & SUPPORTED_FILE_TYPES) + return true; + else + return false; + } + + // Sets this ELFStubFile's type to a supported format. + Error setFileType(FileType NewType) { + if (supportsType(NewType)) { + Type = NewType; + return Error::success(); + } else { + return make_error( + "Invalid file type for ELF stub.", + std::make_error_code(std::errc::invalid_argument)); + } + } + + static const uint32_t SUPPORTED_FILE_TYPES = + FileType::TBE_V1 | FileType::ELF_32LE | FileType::ELF_32BE | + FileType::ELF_64LE | FileType::ELF_64BE; + + ELFStub getStub() const { + return Stub; + } + +private: + ELFStub Stub; + enum FileType Type; + + static bool symbolCompareTo(const ELFSymbol &LHS, const ELFSymbol &RHS) { + return LHS.StName < RHS.StName; + } +}; + +} // end namespace tapi +} // end namespace llvm + +#endif // LLVM_TAPI_ELFSTUBFILE_H Index: llvm/include/llvm/TAPI/TBEHandler.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/TBEHandler.h @@ -0,0 +1,36 @@ +//===- TBEHandler.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 .tbe (text-based ELF) files. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TAPI_TBEHANDLER_H +#define LLVM_TAPI_TBEHANDLER_H + +#include "llvm/Support/Error.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/TAPI/ELFStubFile.h" + +namespace llvm { +namespace tapi { + +class TBEHandler { +public: + bool canRead(StringRef BufferRef) const; + FileType getFileType(StringRef BufferRef) const; + std::unique_ptr readFile(StringRef BufferRef); + Error writeFile(raw_ostream &OS, const ELFStubFile *File); +}; + +} // end namespace tapi +} // end namespace llvm + +#endif // LLVM_TAPI_TBEHANDLER_H Index: llvm/lib/CMakeLists.txt =================================================================== --- llvm/lib/CMakeLists.txt +++ llvm/lib/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(LineEditor) add_subdirectory(ProfileData) add_subdirectory(Passes) +add_subdirectory(TAPI) add_subdirectory(ToolDrivers) add_subdirectory(XRay) add_subdirectory(Testing) Index: llvm/lib/LLVMBuild.txt =================================================================== --- llvm/lib/LLVMBuild.txt +++ llvm/lib/LLVMBuild.txt @@ -40,6 +40,7 @@ ProfileData Support TableGen + TAPI Target Testing ToolDrivers Index: llvm/lib/TAPI/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/lib/TAPI/CMakeLists.txt @@ -0,0 +1,6 @@ +add_llvm_library(LLVMTAPI + TBEHandler.cpp + + ADDITIONAL_HEADER_DIRS + "${LLVM_MAIN_INCLUDE_DIR}/llvm/TAPI" +) Index: llvm/lib/TAPI/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/lib/TAPI/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/Support/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 = Library +name = TAPI +parent = Libraries +required_libraries = Object Support Index: llvm/lib/TAPI/TBEHandler.cpp =================================================================== --- /dev/null +++ llvm/lib/TAPI/TBEHandler.cpp @@ -0,0 +1,141 @@ +//===- TBEHandler.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#include "llvm/Support/Error.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/TAPI/TBEHandler.h" +#include "llvm/TAPI/ELFStubFile.h" + +using namespace llvm; +using namespace tapi; + +LLVM_YAML_STRONG_TYPEDEF(uint16_t, ELFArchMapper); + +namespace llvm { +namespace yaml { + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, ELFSymbolType &SymbolType) { + IO.enumCase(SymbolType, "NoType", ELFSymbolType::NoType); + IO.enumCase(SymbolType, "Func", ELFSymbolType::Func); + IO.enumCase(SymbolType, "Object", ELFSymbolType::Object); + IO.enumCase(SymbolType, "TLS", ELFSymbolType::TLS); + IO.enumCase(SymbolType, "Unknown", ELFSymbolType::Unknown); + } +}; + +template<> struct ScalarTraits { + static void output(const ELFArchMapper &Value, void*, + llvm::raw_ostream &Out) { + static const std::map ArchMap = { + {(uint16_t) ELF::EM_X86_64, "x86_64"}, + {(uint16_t) ELF::EM_AARCH64, "AArch64"}, + {(uint16_t) ELF::EM_NONE, "Unknown"} + }; + if (ArchMap.count(Value) == 0) + Out << ArchMap.at(ELF::EM_NONE); + else + Out << ArchMap.at(Value); + } + + static StringRef input(StringRef Scalar, void*, ELFArchMapper &Value) { + static const std::map ArchMap = { + {"x86_64", (uint16_t) ELF::EM_X86_64}, + {"AArch64", (uint16_t) ELF::EM_AARCH64}, + {"Unknown", (uint16_t) ELF::EM_NONE} + }; + if (ArchMap.count(Scalar) == 0) { + Value = ArchMap.at("Unknown"); + return StringRef("Unknown architecture"); + } else { + Value = ArchMap.at(Scalar); + } + + // Returning empty StringRef indicates successful parse. + return StringRef(); + } + + // Don't place quotation marks around Architecture entry. + static QuotingType mustQuote(StringRef) { return QuotingType::None; } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, ELFSymbol &Symbol) { + IO.mapRequired("Type", Symbol.StType); + if (Symbol.StType == ELFSymbolType::Unknown) { + IO.mapOptional("Size", Symbol.StSize, 0u); + } else if (Symbol.StType == ELFSymbolType::Func) { + Symbol.StSize = 0; + } else { + IO.mapRequired("Size", Symbol.StSize); + } + IO.mapOptional("Undefined", Symbol.StUndefined, false); + IO.mapOptional("Warning", Symbol.StWarning); + } + + // compacts symbol information into a single line + static const bool flow = true; +}; + +template <> struct CustomMappingTraits> { + static void inputOne(IO &IO, StringRef Key, + std::vector &List) { + ELFSymbol Sym; + Sym.StName = Key.str(); + List.push_back(Sym); + IO.mapRequired(Key.str().c_str(), List.back()); + } + + static void output(IO &IO, std::vector &List) { + // force "Symbols: {}" if empty + // TODO: fix this in YAMLTraits + if (List.size() == 0) { + IO.beginFlowMapping(); + IO.endFlowMapping(); + } + + for (auto &Sym : List) + IO.mapRequired(Sym.StName.c_str(), Sym); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, ELFStub &Stub) { + if (!IO.mapTag("!tapi-tbe-v1", true)) { + IO.setError("Invalid tbe version"); + } + IO.mapRequired("SoName", Stub.SoName); + IO.mapRequired("Arch", (ELFArchMapper&) Stub.Arch); + IO.mapOptional("Symbols", Stub.Symbols); + } +}; + +} // end namespace yaml +} // end namespace llvm + +std::unique_ptr TBEHandler::readFile(StringRef BufferRef) { + yaml::Input YamlIn(BufferRef); + ELFStub Stub; + YamlIn >> Stub; + if (YamlIn.error()) + return nullptr; + return std::unique_ptr(new ELFStubFile(FileType::TBE_V1, Stub)); +} + +Error TBEHandler::writeFile(raw_ostream &OS, const ELFStubFile *File) { + if (File == nullptr) { + return make_error("Can't write null file.", + std::make_error_code(std::errc::invalid_argument)); + } + yaml::Output YamlOut(OS, NULL, /*WrapColumn =*/ 0); + ELFStub Result = File->getStub(); + YamlOut << Result; + return Error::success(); +} Index: llvm/unittests/CMakeLists.txt =================================================================== --- llvm/unittests/CMakeLists.txt +++ llvm/unittests/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(Passes) add_subdirectory(ProfileData) add_subdirectory(Support) +add_subdirectory(TAPI) add_subdirectory(Target) add_subdirectory(Transforms) add_subdirectory(XRay) Index: llvm/unittests/TAPI/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/unittests/TAPI/CMakeLists.txt @@ -0,0 +1,7 @@ +set(LLVM_LINK_COMPONENTS + TAPI +) + +add_llvm_unittest(TapiTests + YAMLTest.cpp +) Index: llvm/unittests/TAPI/YAMLTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/TAPI/YAMLTest.cpp @@ -0,0 +1,223 @@ +//===- llvm/unittests/TAPI/YAMLTest.cpp -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#include "llvm/ADT/StringRef.h" +#include "llvm/TAPI/ELFStubFile.h" +#include "llvm/TAPI/TBEHandler.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::tapi; + +ELFStub readFromBuffer(const char Data[]) { + TBEHandler Handler; + + StringRef Buf = StringRef(Data); + + auto File = Handler.readFile(Buf); + EXPECT_NE(File.get(), nullptr); + EXPECT_EQ(File->getFileType(), FileType::TBE_V1); + return File->getStub(); +} + +void compareByLine(StringRef LHS, StringRef RHS) { + StringRef Line1; + StringRef Line2; + while (LHS.size() > 0 && RHS.size() > 0) { + std::tie(Line1, LHS) = LHS.split('\n'); + std::tie(Line2, RHS) = RHS.split('\n'); + // This forces null terminated strings. + std::string LeftLine = Line1.str(); + std::string RightLine = Line2.str(); + EXPECT_STREQ(LeftLine.data(), RightLine.data()); + } +} + +TEST(LLVMTAPI, YAMLReadableTBE) { + const char DATA[] = "--- !tapi-tbe-v1\n" + "SoName: test.so\n" + "Arch: x86_64\n" + "Symbols:\n" + " foo: { Type: Func, Undefined: true }\n" + "...\n"; + StringRef Buf = StringRef(DATA); + TBEHandler Handler; + auto File = Handler.readFile(Buf); + EXPECT_NE(File.get(), nullptr); +} + +TEST(LLVMTAPI, YAMLReadsSymbols) { + const unsigned int NUM_SYMS = 5; + const char DATA[] = "--- !tapi-tbe-v1\n" + "SoName: test.so\n" + "Arch: x86_64\n" + "Symbols:\n" + " bar: { Type: Object, Size: 42 }\n" + " baz: { Type: Object, Size: 3 }\n" + " foo: { Type: Func, Warning: \"Deprecated!\" }\n" + " nor: { Type: Func, Undefined: true }\n" + " not: { Type: Object, Undefined: true, Size: 111 }\n" + "...\n"; + ELFStub Stub = readFromBuffer(DATA); + EXPECT_EQ(NUM_SYMS, Stub.Symbols.size()); + + auto SymFoo = Stub.Symbols[2]; + EXPECT_STREQ(SymFoo.StName.c_str(), "foo"); + EXPECT_EQ(SymFoo.StSize, 0u); + EXPECT_EQ(SymFoo.StType, ELFSymbolType::Func); + EXPECT_FALSE(SymFoo.StUndefined); + EXPECT_TRUE(SymFoo.StWarning.hasValue()); + EXPECT_STREQ(SymFoo.StWarning->c_str(), "Deprecated!"); + + auto SymBar = Stub.Symbols[0]; + EXPECT_STREQ(SymBar.StName.c_str(), "bar"); + EXPECT_EQ(SymBar.StSize, 42u); + EXPECT_EQ(SymBar.StType, ELFSymbolType::Object); + EXPECT_FALSE(SymBar.StUndefined); + EXPECT_FALSE(SymBar.StWarning.hasValue()); + + auto SymNot = Stub.Symbols[4]; + EXPECT_STREQ(SymNot.StName.c_str(), "not"); + EXPECT_EQ(SymNot.StSize, 111u); + EXPECT_EQ(SymNot.StType, ELFSymbolType::Object); + EXPECT_TRUE(SymNot.StUndefined); + EXPECT_FALSE(SymNot.StWarning.hasValue()); +} + +TEST(LLVMTAPI, YAMLReadsNoSyms) { + const unsigned int NUM_SYMS = 0; + const char DATA[] = "--- !tapi-tbe-v1\n" + "SoName: test.so\n" + "Arch: x86_64\n" + "Symbols: {}\n" + "...\n"; + ELFStub Stub = readFromBuffer(DATA); + EXPECT_EQ(NUM_SYMS, Stub.Symbols.size()); +} + +TEST(LLVMTAPI, YAMLReadsArchitecture) { + const char DATA[] = "--- !tapi-tbe-v1\n" + "SoName: test.so\n" + "Arch: x86_64\n" + "Symbols:\n" + " foo: { Type: Func, Undefined: true }\n" + "...\n"; + + ELFStub Stub = readFromBuffer(DATA); + EXPECT_EQ(Stub.Arch, (uint16_t) llvm::ELF::EM_X86_64); +} + +TEST(LLVMTAPI, YAMLReadsSOName) { + const char DATA[] = "--- !tapi-tbe-v1\n" + "SoName: test.so\n" + "Arch: x86_64\n" + "Symbols:\n" + " foo: { Type: Func, Undefined: true }\n" + "...\n"; + + ELFStub Stub = readFromBuffer(DATA); + EXPECT_STREQ(Stub.SoName.c_str(), "test.so"); +} + +TEST(LLVMTAPI, YAMLUnreadableTBE) { + TBEHandler Handler; + // Can't read: wrong format/version. + const char DATA[] = "--- !ti-tbe-v42\n" + "SoName: test.so\n" + "Arch: x86_64\n" + "Symbols:\n" + " foo: { Type: Func, Undefined: true }\n"; + StringRef Buf = StringRef(DATA); + auto File = Handler.readFile(Buf); + EXPECT_EQ(File.get(), nullptr); +} + +TEST(LLVMTAPI, YAMLWritesSymbols) { + const char EXPECTED[] = + "--- !tapi-tbe-v1\n" + "SoName: test.so\n" + "Arch: x86_64\n" + "Symbols: \n" + " bar: { Type: Object, Size: 42 }\n" + " baz: { Type: Object, Size: 3 }\n" + " foo: { Type: Func, Warning: \'Deprecated!\' }\n" + " nor: { Type: Func, Undefined: true }\n" + " not: { Type: Object, Size: 123456789, Undefined: true }\n" + "...\n"; + ELFStub Stub; + Stub.SoName = "test.so"; + Stub.Arch = ELF::EM_X86_64; + + ELFSymbol SymFoo; + SymFoo.StName = "foo"; + SymFoo.StSize = 0u; + SymFoo.StType = ELFSymbolType::Func; + SymFoo.StUndefined = false; + SymFoo.StWarning = "Deprecated!"; + + ELFSymbol SymBar; + SymBar.StName = "bar"; + SymBar.StSize = 42u; + SymBar.StType = ELFSymbolType::Object; + SymBar.StUndefined = false; + + ELFSymbol SymBaz; + SymBaz.StName = "baz"; + SymBaz.StSize = 3u; + SymBaz.StType = ELFSymbolType::Object; + SymBaz.StUndefined = false; + + ELFSymbol SymNot; + SymNot.StName = "not"; + SymNot.StSize = 123456789u; + SymNot.StType = ELFSymbolType::Object; + SymNot.StUndefined = true; + + ELFSymbol SymNor; + SymNor.StName = "nor"; + SymNor.StType = ELFSymbolType::Func; + SymNor.StUndefined = true; + + Stub.Symbols.push_back(SymFoo); + Stub.Symbols.push_back(SymBar); + Stub.Symbols.push_back(SymBaz); + Stub.Symbols.push_back(SymNot); + Stub.Symbols.push_back(SymNor); + + std::string Result; + raw_string_ostream OS(Result); + ELFStubFile File(FileType::TBE_V1, Stub); + TBEHandler Handler; + EXPECT_FALSE(Handler.writeFile(OS, &File)); + Result = OS.str(); + compareByLine(Result.c_str(), EXPECTED); +} + +TEST(LLVMTAPI, YAMLWritesNoSyms) { + const char EXPECTED[] = + "--- !tapi-tbe-v1\n" + "SoName: nosyms.so\n" + "Arch: x86_64\n" + "Symbols: \n" + " { }\n" + "...\n"; + ELFStub Stub; + Stub.SoName = "nosyms.so"; + Stub.Arch = ELF::EM_X86_64; + + std::string Result; + raw_string_ostream OS(Result); + ELFStubFile File(FileType::TBE_V1, Stub); + TBEHandler Handler; + EXPECT_FALSE(Handler.writeFile(OS, &File)); + Result = OS.str(); + compareByLine(Result.c_str(), EXPECTED); +}