Index: llvm/include/llvm/TAPI/ELFStubFile.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/ELFStubFile.h @@ -0,0 +1,122 @@ +//===- 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 interface for interacting with an internal +// representation of an ELF stub. This object doesn't support casting. +// +// This file should support most of the desired API for accessing/populating +// ELF stubs. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TAPI_ELFSTUBFILE_H +#define LLVM_TAPI_ELFSTUBFILE_H + +#include "llvm/Object/ELF.h" +#include "llvm/TAPI/File.h" + +#include + +namespace llvm { +namespace tapi { + +typedef uint16_t ELFArch; + +enum ELFSymbolType { + no_type = ELF::STT_NOTYPE, + object = ELF::STT_OBJECT, + function = ELF::STT_FUNC, + tls = ELF::STT_TLS, + + // Type information is a nibble, so 16 is safely out of range. + unknown = 16, +}; + +// TODO: inefficient, improve. +struct ELFSymbol { + std::string st_name; + unsigned int st_size; + ELFSymbolType st_type; + bool st_undefined; + std::string st_warning; +}; + +// TODO: inefficient, improve. +struct ELFStub { + std::map symbols; + ELFArch arch; + std::string so_name; +}; + +// A cumulative representation of ELF stubs. +// Both textual and binary stubs will read into and write from this object. +class ELFStubFile : public File { +public: + ELFStubFile(FileType type) : File(type) {} + ELFStubFile(FileType type, ELFStub &stub) : File(type) { + _symbols.clear(); + _arch = stub.arch; + _so_name = stub.so_name; + for (auto it = stub.symbols.begin(); it != stub.symbols.end(); ++it) { + _symbols.push_back(it->second); + } + } + + // Returns all FileTypes this file can be converted among. + uint32_t getCompatibleTypes() const override { return SUPPORTED_FILE_TYPES; } + + // Returns original FileType of this file. + enum FileType getFileType() const override { return _type; } + + // Returns whether or not type is a compatible type. + bool canConvertTo(FileType type) const override { + if (type & SUPPORTED_FILE_TYPES) + return true; + else + return false; + } + + Error convertTo(FileType type) override { + if (canConvertTo(type)) { + _type = type; + return Error::success(); + } else { + return make_error( + "Invalid file type for ELF stub file conversion", + std::make_error_code(std::errc::invalid_argument)); + } + } + + static bool classof(const File* f) { return true;} + + static const uint32_t SUPPORTED_FILE_TYPES = + FileType::TBE_V1 | FileType::ELF_32LE | FileType::ELF_32BE | + FileType::ELF_64LE | FileType::ELF_64BE; + + // TODO: redesign so this is not needed? + ELFStub getStub() const { + ELFStub stub; + stub.arch = _arch; + stub.so_name = _so_name; + for (auto elem : _symbols) { + stub.symbols.insert(std::make_pair(elem.st_name, elem)); + } + return stub; + } + +private: + std::vector _symbols; + ELFArch _arch; + std::string _so_name; +}; + +} // end namespace tapi +} // end namespace llvm + +#endif // LLVM_TAPI_ELFSTUBFILE_H Index: llvm/include/llvm/TAPI/File.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/File.h @@ -0,0 +1,68 @@ +//===- File.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ +// +// Represents a File that has been read in by llvm-tapi. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TAPI_FILE_H +#define LLVM_TAPI_FILE_H + +#include "llvm/Support/Path.h" + +namespace llvm { +namespace tapi { + +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, +}; + +class File { +public: + virtual ~File() {} + + void setFilePath(StringRef path) { _path = path; } + StringRef getFilePath() const { return _path; } + StringRef getFileName() const { return llvm::sys::path::filename(_path); } + + // Returns FileType of the File. + virtual FileType getFileType() const = 0; + + // Returns all FileTypes the File can be converted among. + virtual uint32_t getCompatibleTypes() const = 0; + + // Returns true if the File can convert to `type`. + virtual bool canConvertTo(FileType type) const = 0; + + // Converts this File to `type` if supported. + virtual Error convertTo(FileType type) = 0; + +protected: + File(FileType t) { _type = t; } + enum FileType _type; + +private: + StringRef _path; +}; + +} // end namespace tapi +} // end namespace llvm + +#endif // LLVM_TAPI_FILE_H Index: llvm/include/llvm/TAPI/FileHandler.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/FileHandler.h @@ -0,0 +1,56 @@ +//===- FileHandler.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ +// +// This is the exposed interface for File read/write handlers. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TAPI_FILEHANDLER_H +#define LLVM_TAPI_FILEHANDLER_H + +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TAPI/File.h" + +namespace llvm { +namespace tapi { + +// API all FileHandlers should implement. +class FileHandler { +public: + + virtual ~FileHandler() {} + + // Ensures a file can be read by this object. + virtual bool canRead(MemoryBufferRef memBufferRef, + FileType types = FileType::All) const = 0; + + // Ensures a file can be written by this object. + virtual bool canWrite(const File *file) const = 0; + + // Returns the file type of the file in the passed buffer if known. + virtual FileType getFileType(MemoryBufferRef memBufferRef) const = 0; + + // Returns all supported file extensions. + virtual std::vector getSupportedFileExtensions() const = 0; + + // Attempts to read the file. + virtual std::unique_ptr readFile(MemoryBufferRef memBufferRef) = 0; + + // Attempts to write to a file. + virtual Error writeFile(raw_ostream &os, const File *file) = 0; + +protected: + FileHandler(){}; +}; + +} // end namespace tapi +} // end namespace llvm + +#endif // LLVM_TAPI_FILEHANDLER_H Index: llvm/include/llvm/TAPI/TBEHandlerV1.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/TBEHandlerV1.h @@ -0,0 +1,42 @@ +//===- TBEHandlerV1.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_TBEHandlerV1_H +#define LLVM_TAPI_TBEHandlerV1_H + +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/TAPI/File.h" +#include "llvm/TAPI/FileHandler.h" + +LLVM_YAML_STRONG_TYPEDEF(uint16_t, ELFArchMapper); + +namespace llvm { +namespace tapi { + +class TBEHandlerV1 : public FileHandler { +public: + bool canRead(MemoryBufferRef memBufferRef, + FileType types = FileType::All) const override; + bool canWrite(const File *file) const override; + FileType getFileType(MemoryBufferRef memBufferRef) const override; + std::vector getSupportedFileExtensions() const override; + std::unique_ptr readFile(MemoryBufferRef memBufferRef) override; + Error writeFile(raw_ostream &os, const File *file) override; +}; + +} // end namespace tapi +} // end namespace llvm + +#endif // LLVM_TAPI_TBEHandlerV1_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 + TBEHandlerV1.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/TBEHandlerV1.cpp =================================================================== --- /dev/null +++ llvm/lib/TAPI/TBEHandlerV1.cpp @@ -0,0 +1,162 @@ +//===- TBEHandlerV1.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/Support/MemoryBuffer.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/TAPI/TBEHandlerV1.h" +#include "llvm/TAPI/ELFStubFile.h" +#include "llvm/TAPI/File.h" +#include "llvm/TAPI/FileHandler.h" + +using llvm::yaml::IO; + +using namespace llvm; +using namespace tapi; + +namespace llvm { +namespace yaml { + +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &io, ELFSymbolType &symbolType) { + io.enumCase(symbolType, "NO-TYPE", ELFSymbolType::no_type); + io.enumCase(symbolType, "FUNCTION", ELFSymbolType::function); + 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) { + if (value == (uint16_t)ELF::EM_AARCH64) + out << "AARCH64"; + else if (value == (uint16_t) ELF::EM_X86_64) + out << "X86_64"; + else + out << "UNKNOWN"; + } + + static StringRef input(StringRef scalar, void*, ELFArchMapper &value) { + if (scalar.equals("AARCH64")) + value = ELF::EM_AARCH64; + else if (scalar.equals("X86_64")) + value = ELF::EM_X86_64; + else + value = ELF::EM_NONE; + return StringRef(); + } + + static QuotingType mustQuote(StringRef) { return QuotingType::None; } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, ELFSymbol &symbol) { + io.mapRequired("TYPE", symbol.st_type); + io.mapOptional("SIZE", symbol.st_size, 0u); + io.mapOptional("UNDEFINED", symbol.st_undefined, false); + io.mapOptional("WARNING-TEXT", symbol.st_warning, std::string("")); + + // clear size for functions + if (symbol.st_type == ELFSymbolType::function) + symbol.st_size = 0; + } + + static const bool flow = true; +}; + +template <> struct CustomMappingTraits> { + static void inputOne(IO &io, StringRef key, + std::map &map) { + // Properly insert symbol with name before mapping. + if (map.find(key) == map.end()) { + ELFSymbol elem; + elem.st_name = key; + map.insert(std::make_pair(key, elem)); + } + io.mapRequired(key.str().c_str(), map[key]); + } + + static void output(IO &io, std::map &map) { + for (auto &elem : map) + io.mapRequired(elem.first.c_str(), elem.second); + } +}; + +template <> struct MappingTraits { + static void mapping(IO &io, ELFStub &stub) { + io.mapTag("!tapi-tbe-v1", true); + io.mapRequired("SONAME", stub.so_name); + io.mapRequired("ARCHITECTURE", (ELFArchMapper&) stub.arch); + if (io.outputting() && stub.symbols.size() > 0) + io.mapRequired("SYMBOLS", stub.symbols); + else if (!io.outputting()) + io.mapOptional("SYMBOLS", stub.symbols); + } +}; + +} // end namespace yaml +} // end namespace llvm + +bool TBEHandlerV1::canRead(MemoryBufferRef memBufferRef, FileType types) const { + if (!(types & FileType::TBE_V1)) + return false; + + StringRef str = memBufferRef.getBuffer().trim(); + // startswith() and endswith() aren't options as they break testing via lit. + if (str.find("--- !tapi-tbe-v1\n") != StringLiteral::npos + && str.find("...") != StringLiteral::npos) + return true; + + return false; +} + +bool TBEHandlerV1::canWrite(const File *file) const { + if (FileType::TBE_V1 & file->getFileType()) + return true; + + return false; +} + +FileType TBEHandlerV1::getFileType( + MemoryBufferRef memBufferRef) const { + if (canRead(memBufferRef)) + return FileType::TBE_V1; + + return FileType::Invalid; +} + +std::vector TBEHandlerV1::getSupportedFileExtensions() const { + return {".tbe"}; +} + +std::unique_ptr TBEHandlerV1::readFile( + MemoryBufferRef memBufferRef) { + if (!canRead(memBufferRef, FileType::All)) + return nullptr; + yaml::Input yamlIn(memBufferRef); + ELFStub stub; + yamlIn >> stub; + if (yamlIn.error()) + return nullptr; + return std::unique_ptr(new ELFStubFile(FileType::TBE_V1, stub)); +} + +Error TBEHandlerV1::writeFile(raw_ostream &os, const File *file) { + auto *inFile = dyn_cast_or_null(file); + if (inFile == nullptr) { + return make_error("File couldn't be cast to ELF stub file!", + std::make_error_code(std::errc::invalid_argument)); + } + yaml::Output yamlOut(os, NULL, /*WrapColumn =*/ 0); + ELFStub result = inFile->getStub(); + yamlOut << result; + return Error::success(); +} Index: llvm/test/CMakeLists.txt =================================================================== --- llvm/test/CMakeLists.txt +++ llvm/test/CMakeLists.txt @@ -88,6 +88,7 @@ llvm-strings llvm-strip llvm-symbolizer + llvm-tapi llvm-tblgen llvm-undname llvm-xray Index: llvm/test/tools/llvm-tapi/basic-tbe-read.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi/basic-tbe-read.test @@ -0,0 +1,23 @@ +# RUN: llvm-tapi %s | FileCheck %s + +--- !tapi-tbe-v1 +SONAME: somelib.so +ARCHITECTURE: X86_64 +SYMBOLS: + foo: { TYPE: FUNCTION} + bar: { TYPE: OBJECT, SIZE: 42 } + baz: { TYPE: OBJECT } + not: { TYPE: OBJECT, UNDEFINED: true } + nor: { TYPE: FUNCTION, UNDEFINED: true, SIZE: 42} +... + +#CHECK: --- !tapi-tbe-v1 +#CHECK-NEXT: SONAME: somelib.so +#CHECK-NEXT: ARCHITECTURE: X86_64 +#CHECK-NEXT: SYMBOLS: +#CHECK-NEXT: bar: { TYPE: OBJECT, SIZE: 42 } +#CHECK-NEXT: baz: { TYPE: OBJECT } +#CHECK-NEXT: foo: { TYPE: FUNCTION } +#CHECK-NEXT: nor: { TYPE: FUNCTION, UNDEFINED: true } +#CHECK-NEXT: not: { TYPE: OBJECT, UNDEFINED: true } +#CHECK-NEXT: ... Index: llvm/tools/LLVMBuild.txt =================================================================== --- llvm/tools/LLVMBuild.txt +++ llvm/tools/LLVMBuild.txt @@ -50,6 +50,7 @@ llvm-rtdyld llvm-size llvm-split + llvm-tapi llvm-undname opt verify-uselistorder Index: llvm/tools/llvm-tapi/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-tapi/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + TAPI + ) + +add_llvm_tool(llvm-tapi + llvm-tapi.cpp + ) Index: llvm/tools/llvm-tapi/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-tapi/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-tapi/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-tapi +parent = Tools +required_libraries = TAPI Index: llvm/tools/llvm-tapi/llvm-tapi.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-tapi/llvm-tapi.cpp @@ -0,0 +1,50 @@ +// a simple binary to test tapi, will evolve into the tapi command line utility. +#include +#include "llvm/TAPI/TBEHandlerV1.h" +#include "llvm/TAPI/File.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::tapi; + +// TODO: clean up and make useful +int main(int argc, char *argv[]) { + TBEHandlerV1 handler; + if (argc < 2) { + llvm::outs() << "too few arguments\n"; + return 1; + } + StringRef InFile = argv[1]; + + auto BufOrError = MemoryBuffer::getFile(InFile); + if (!BufOrError) { + llvm::outs() << "Couldn't open input file: "; + llvm::outs() << BufOrError.getError().message() << "\n"; + return 1; + } + + auto InBuf = BufOrError.get()->getMemBufferRef(); + + if (!handler.canRead(InBuf)) { + llvm::outs() << "Error: input file format not currently supported!\n"; + return 1; + } + auto tbeFile = handler.readFile(InBuf); + if (tbeFile == nullptr) { + llvm::outs() << "Error: failed to read file!\n"; + return 1; + } + + if (tbeFile->convertTo(FileType::TBE_V1)) { + llvm::outs() << "Can't convert to .tbe!\n"; + return 1; + } + + if (handler.writeFile(llvm::outs(), tbeFile.get())) { + llvm::outs() << "Error: Failed to write to llvm::outs()!\n"; + return 1; + } + return 0; +} 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,61 @@ +//===- 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/Support/MemoryBuffer.h" +#include "llvm/TAPI/TBEHandlerV1.h" +#include "gtest/gtest.h" +#include + +using llvm::MemoryBuffer; +using llvm::tapi::TBEHandlerV1; + +TEST(LLVMTAPI, YAMLReadTBE) { + const char data[] = "--- !tapi-tbe-v1\n" + "SONAME: test.so\n" + "ARCHITECTURE: X86_64\n" + "SYMBOLS:\n" + " foo: { TYPE: FUNCTION, UNDEFINED: true }\n" + "...\n"; + std::unique_ptr buf = MemoryBuffer::getMemBuffer(data); + TBEHandlerV1 handler; + + EXPECT_TRUE(handler.canRead(buf->getMemBufferRef())); + + auto file = handler.readFile(buf->getMemBufferRef()); + EXPECT_NE(nullptr, file.get()); +} + +TEST(LLVMTAPI, YAMLUnreadableTBE) { + TBEHandlerV1 handler; + // Can't read: wrong format/version. + const char data[] = "--- !ti-tbe-v42\n" + "SONAME: test.so\n" + "ARCHITECTURE: X86_64\n" + "SYMBOLS:\n" + " foo: { TYPE: FUNCTION, UNDEFINED: true }\n"; + std::unique_ptr buf = MemoryBuffer::getMemBuffer(data); + EXPECT_FALSE(handler.canRead(buf->getMemBufferRef())); + auto file = handler.readFile(buf->getMemBufferRef()); + EXPECT_EQ(nullptr, file.get()); +} + +TEST(LLVMTAPI, YAMLMalformedTBE) { + TBEHandlerV1 handler; + // Can read, but malformed. + const char data[] = "--- !tapi-tbe-v1\n" + "SONAME: test.so\n" + "arch: X86_64\n" + "SYMBOLS:\n" + " foo: { TYPE: FU{NCTION, UNDEFINED: true }\n" + "...\n"; + std::unique_ptr buf = MemoryBuffer::getMemBuffer(data); + EXPECT_TRUE(handler.canRead(buf->getMemBufferRef())); + auto file = handler.readFile(buf->getMemBufferRef()); + EXPECT_EQ(nullptr, file.get()); +}