Index: llvm/include/llvm/TAPI/ELFObjHandler.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/ELFObjHandler.h @@ -0,0 +1,79 @@ +//===- 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_TAPI_ELFOBJHANDLER_H +#define LLVM_TAPI_ELFOBJHANDLER_H + +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TAPI/ELFStubFile.h" +#include "llvm/TAPI/File.h" +#include "llvm/TAPI/FileHandler.h" + +namespace llvm { +namespace tapi { + +template class ELFStubBuilder { +public: + using Elf_Ehdr = typename ELFT::Ehdr; + using Elf_Sym = typename ELFT::Sym; + using Elf_Dyn = typename ELFT::Dyn; + using Elf_Sym_Range = typename ELFT::SymRange; + using Elf_Dyn_Range = typename ELFT::DynRange; + ELFStubBuilder(const object::ELFObjectFile *ElfObj, ELFStub &Stub) + : _stub(Stub), _obj(ElfObj) {} + ELFStubBuilder(ELFStub &Stub) : _stub(Stub), _obj(nullptr) {} + Error build(); + void populateArch(const Elf_Ehdr *Header); + Error populateSOName(const Elf_Dyn_Range DynTab, const StringRef DynStr); + Error populateSymbols(const Elf_Sym_Range DynSym, const StringRef DynStr); + Expected buildSymbol(const Elf_Sym *rawSym, + const StringRef DynStr); + ELFSymbolType convertInfoToType(unsigned char info); + +private: + ELFStub &_stub; + const object::ELFObjectFile *_obj; +}; + +class ELFObjHandler : public FileHandler { +public: + bool canRead(MemoryBufferRef memBufferRef, + FileType types) 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; + llvm::Error writeFile(raw_ostream &os, const File *file) override; + static const uint32_t SUPPORTED_FILE_TYPES = + FileType::ELF_32LE | FileType::ELF_32BE | + FileType::ELF_64LE | FileType::ELF_64BE; +private: + class BinaryELFReader { + public: + static Expected> + read(MemoryBufferRef memBufferRef); + }; + + class BinaryELFWriter { + public: + static llvm::Error write(raw_ostream &os, const ELFStub &stub); + }; +}; + +} // end namespace tapi +} // end namespace llvm + +#endif // LLVM_TAPI_ELFOBJHANDLER_H 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/ELFTextStubHandler.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/ELFTextStubHandler.h @@ -0,0 +1,42 @@ +//===- ELFTextStubHandler.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_ELFTEXTSTUBHANDLER_H +#define LLVM_TAPI_ELFTEXTSTUBHANDLER_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 ELFTextStubHandler : 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_ELFTEXTSTUBHANDLER_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/Registry.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/Registry.h @@ -0,0 +1,65 @@ +//===- Registry.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 should eventually collect all FileHandlers under one umbrella and +// report various functionality. This provides a more abstracted interface for +// handling Files. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TAPI_REGISTRY_H +#define LLVM_TAPI_REGISTRY_H + +#include "llvm/Support/Error.h" +#include "llvm/TAPI/File.h" +#include "llvm/TAPI/FileHandler.h" + +namespace llvm { +namespace tapi { + +// Overarching interface for reading/writing files. +class Registry { +public: + + // Returns all supported file extensions. + std::vector getSupportedFileExtensions() const; + + // Ensures a file can be read by this object. + bool canRead(llvm::MemoryBufferRef memBufferRef, + FileType types = FileType::All) const; + + // Ensures a file can be written by this object. + bool canWrite(const File *file) const; + + // Returns the file type of the file in the passed buffer if known. + FileType getFileType(MemoryBufferRef memBufferRef) const; + + // Attempts to read a file stored in a memory buffer ref into a File object of + // appropriate type. + std::unique_ptr readFile(MemoryBufferRef memBufferRef) const; + + // Attempts to write to a file. + llvm::Error writeFile(raw_ostream &os, const File *file) const; + + // Attempts to write to a file as a specific FileType. + llvm::Error writeFileAs(raw_ostream &os, + const File *file, FileType type) const; + + void addAllHandlers(); + void addAllYAMLHandlers(); + void addAllBinaryHandlers(); + +private: + std::vector> _handlers; +}; + +} // end namespace tapi +} // end namespace llvm + +#endif // LLVM_TAPI_REGISTRY_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,8 @@ +add_llvm_library(LLVMTAPI + ELFTextStubHandler.cpp + ELFObjHandler.cpp + Registry.cpp + + ADDITIONAL_HEADER_DIRS + "${LLVM_MAIN_INCLUDE_DIR}/llvm/TAPI" +) Index: llvm/lib/TAPI/ELFObjHandler.cpp =================================================================== --- /dev/null +++ llvm/lib/TAPI/ELFObjHandler.cpp @@ -0,0 +1,265 @@ +//===- ELFObjHandler.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/BinaryFormat/Magic.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TAPI/ELFObjHandler.h" +#include "llvm/TAPI/ELFStubFile.h" +#include "llvm/TAPI/File.h" +#include "llvm/TAPI/FileHandler.h" +#include + +using llvm::MemoryBufferRef; +using llvm::object::ELFObjectFile; + +using namespace llvm; +using namespace tapi; +using namespace object; + +template Error ELFStubBuilder::build() { + if (_obj == nullptr) + return make_error("Source object is NULL!", + std::make_error_code(std::errc::invalid_argument)); + auto elfFile = _obj->getELFFile(); + + // Fetch needed tables/headers. This includes .dynamic, .dynsym, .dynstr, and + // the header entry for .dynsym. + auto Header = elfFile->getHeader(); + auto DynOrErr = elfFile->dynamicEntries(); + if (!DynOrErr) + return DynOrErr.takeError(); + auto DynSymOrErr = elfFile->getSection(".dynsym"); + if (!DynSymOrErr) + return DynSymOrErr.takeError(); + auto DynStrOrErr = + elfFile->getStringTableForSymtab(*(DynSymOrErr.get())); + if (!DynStrOrErr) + return DynStrOrErr.takeError(); + auto SymsOrErr = elfFile->symbols(DynSymOrErr.get()); + if (!SymsOrErr) + return SymsOrErr.takeError(); + + // populate architecture + populateArch(Header); + + // populate SONAME + if (Error Err = populateSOName(DynOrErr.get(), DynStrOrErr.get())) + return Err; + + // populate symbols + if (Error Err = populateSymbols(SymsOrErr.get(), DynStrOrErr.get())) { + return Err; + } + + return Error::success(); +} + +template void ELFStubBuilder::populateArch( + const Elf_Ehdr *Header) { + _stub.arch = Header->e_machine; +} + +template +Error ELFStubBuilder::populateSOName(const Elf_Dyn_Range DynTab, + const StringRef DynStr) { + // Attempt to find DT_SONAME entry in dynamic table. + const Elf_Dyn* dt_soname = nullptr; + for (auto i = DynTab.begin(); i < DynTab.end(); ++i) { + if (i->getTag() == ELF::DT_SONAME) { + dt_soname = i; + break; + } + } + + // if not found, set DT_SONAME to an empty string. + if (dt_soname == nullptr) { + _stub.so_name = std::string(""); + return Error::success(); + } + + // pull DT_SONAME text from .dynstr + _stub.so_name = + std::string(DynStr.data() + dt_soname->getVal()); + + return Error::success(); +} + +template +Error ELFStubBuilder::populateSymbols(const Elf_Sym_Range DynSym, + const StringRef DynStr) { + // This loop deliberately skips the first element, + // which is the NULL symbol that initiates a symbol table. + auto it = DynSym.begin(); + while (++it < DynSym.end()) { + auto SymbolOrErr = buildSymbol(it, DynStr); + if (!SymbolOrErr) + return SymbolOrErr.takeError(); + _stub.symbols.insert(std::make_pair(SymbolOrErr->st_name, + SymbolOrErr.get())); + } + + return Error::success(); +} + +template +Expected ELFStubBuilder::buildSymbol(const Elf_Sym* rawSym, + const StringRef DynStr) { + ELFSymbol sym; + sym.st_size = 0u; // default size + + // TODO: implement warnings + sym.st_warning = std::string(""); + + // TODO: if hidden, ignore? + + // Fetch symbol name. + if (rawSym->st_name != 0) { + sym.st_name = std::string(DynStr.data() + rawSym->st_name); + } else { + sym.st_name = std::string(""); + llvm::errs() << "Warning: encountered an unnamed symbol!\n"; + } + + // Mark whether or not the symbol is undefined. + if (rawSym->st_shndx == 0) { + sym.st_undefined = true; + } else { + sym.st_undefined = false; + } + + // Fetch symbol type. + sym.st_type = convertInfoToType(rawSym->st_info); + + // Fetch symbol size, if applicable. + if (sym.st_type != ELFSymbolType::function) { + sym.st_size = rawSym->st_size; + } + + return sym; +} + +template +ELFSymbolType ELFStubBuilder::convertInfoToType(unsigned char info) { + info = info & 0xf; + switch (info) { + case ELF::STT_NOTYPE: + return ELFSymbolType::no_type; + case ELF::STT_OBJECT: + return ELFSymbolType::object; + case ELF::STT_FUNC: + return ELFSymbolType::function; + case ELF::STT_TLS: + return ELFSymbolType::tls; + default: + return ELFSymbolType::unknown; + } +} + +Expected> ELFObjHandler::BinaryELFReader::read( + MemoryBufferRef memBufferRef) { + auto stub = make_unique(); + auto BinOrErr = createBinary(memBufferRef); + if (!BinOrErr) { + llvm::errs() << "Couldn't load ELF file!\n"; + return BinOrErr.takeError(); + } + + Binary *bin = BinOrErr.get().get(); + if (auto obj = dyn_cast>(bin)) { + ELFStubBuilder builder(obj, *stub); + if (Error Err = builder.build()) + return std::move(Err); + } else if (auto obj = dyn_cast>(bin)) { + ELFStubBuilder builder(obj, *stub); + if (Error Err = builder.build()) + return std::move(Err); + } else if (auto obj = dyn_cast>(bin)) { + ELFStubBuilder builder(obj, *stub); + if (Error Err = builder.build()) + return std::move(Err); + } else if (auto obj = dyn_cast>(bin)) { + ELFStubBuilder builder(obj, *stub); + if (Error Err = builder.build()) + return std::move(Err); + } else { + return make_error("Unsupported ELF file architecture", + std::make_error_code(std::errc::not_supported)); + } + return stub; +} + +bool ELFObjHandler::canRead(MemoryBufferRef memBufferRef, + FileType types) const { + llvm::file_magic magic = identify_magic(memBufferRef.getBuffer()); + if (magic != file_magic::elf_shared_object) + return false; + else if ((types & SUPPORTED_FILE_TYPES) == 0) + return false; + else + return true; +} + +bool ELFObjHandler::canWrite(const File *file) const { + // TODO: implement writing support, then un-comment: + // if (FileType::ELF_DSO & file->getFileType()) + // return true; + + return false; +} + +FileType ELFObjHandler::getFileType(MemoryBufferRef memBufferRef) const { + llvm::file_magic magic = identify_magic(memBufferRef.getBuffer()); + if (magic == file_magic::elf_shared_object) { + auto BinOrErr = createBinary(memBufferRef); + if (!BinOrErr) { + llvm::errs() << "Couldn't load ELF file!\n"; + Error Err = BinOrErr.takeError(); + return FileType::Invalid; + } + + Binary *bin = BinOrErr->get(); + if (auto obj = dyn_cast>(bin)) { + return FileType::ELF_32LE; + } else if (auto obj = dyn_cast>(bin)) { + return FileType::ELF_32BE; + } else if (auto obj = dyn_cast>(bin)) { + return FileType::ELF_64LE; + } else if (auto obj = dyn_cast>(bin)) { + return FileType::ELF_64BE; + } else { + return FileType::Invalid; + } + } else { + return FileType::Invalid; + } +} + +std::vector ELFObjHandler::getSupportedFileExtensions() const { + return {".abi", ".so"}; +} + +std::unique_ptr ELFObjHandler::readFile(MemoryBufferRef memBufferRef) { + if (!canRead(memBufferRef, FileType::All)) + return nullptr; + FileType Type = getFileType(memBufferRef); + auto stub = BinaryELFReader::read(memBufferRef); + if (!stub) { + llvm::errs() << "Failed reading ELF file: " << stub.takeError() << "\n"; + return nullptr; + } + return make_unique(Type, *(stub.get())); +} + +Error ELFObjHandler::writeFile(raw_ostream &os, const File *file) { + // TODO: implement writing of .so.abi stubs + return make_error("Not yet implemented!", + std::make_error_code(std::errc::not_supported)); +} Index: llvm/lib/TAPI/ELFTextStubHandler.cpp =================================================================== --- /dev/null +++ llvm/lib/TAPI/ELFTextStubHandler.cpp @@ -0,0 +1,164 @@ +//===- ELFTextStubHandler.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/ELFTextStubHandler.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 ELFTextStubHandler::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 ELFTextStubHandler::canWrite(const File *file) const { + if (FileType::TBE_V1 & file->getFileType()) + return true; + + return false; +} + +FileType ELFTextStubHandler::getFileType( + MemoryBufferRef memBufferRef) const { + if (canRead(memBufferRef)) + return FileType::TBE_V1; + + return FileType::Invalid; +} + +std::vector ELFTextStubHandler::getSupportedFileExtensions() + const { + return {".tbe"}; +} + +std::unique_ptr ELFTextStubHandler::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 ELFTextStubHandler::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/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/Registry.cpp =================================================================== --- /dev/null +++ llvm/lib/TAPI/Registry.cpp @@ -0,0 +1,95 @@ +//===- Registry.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/TAPI/ELFObjHandler.h" +#include "llvm/TAPI/ELFTextStubHandler.h" +#include "llvm/TAPI/File.h" +#include "llvm/TAPI/FileHandler.h" +#include "llvm/TAPI/Registry.h" + +using namespace llvm; +using namespace tapi; + +std::vector Registry::getSupportedFileExtensions() const { + // TODO: check for duplicates + std::vector extensions; + for (const auto &handler : _handlers) { + std::vector ext = handler->getSupportedFileExtensions(); + extensions.insert(extensions.end(), ext.begin(), ext.end()); + } + return extensions; +} + +bool Registry::canRead(MemoryBufferRef memBufferRef, + FileType types) const { + for (const auto &handler : _handlers) { + if (handler->canRead(memBufferRef)) + return true; + } + return false; +} + +bool Registry::canWrite(const File *file) const { + for (const auto &handler : _handlers) { + if (handler->canWrite(file)) + return true; + } + return false; +} + +FileType Registry::getFileType(MemoryBufferRef memBufferRef) const { + for (const auto &handler : _handlers) { + if (handler->canRead(memBufferRef)) + return handler->getFileType(memBufferRef); + } + return FileType::Invalid; +} + +std::unique_ptr Registry::readFile(MemoryBufferRef memBufferRef) const { + for (const auto &handler : _handlers) { + if (handler->canRead(memBufferRef)) + return handler->readFile(memBufferRef); + } + return nullptr; +} + +Error Registry::writeFile(raw_ostream &os, const File *file) const { + for (const auto &handler : _handlers) { + if (handler->canWrite(file)) + return handler->writeFile(os, file); + } + return make_error("File type can't be written!", + std::make_error_code(std::errc::not_supported)); +} + +Error Registry::writeFileAs(raw_ostream &os, const File *file, + FileType type) const { + if (!file->canConvertTo(type)) { + return make_error("Failed converting to unsupported FileType!", + std::make_error_code(std::errc::not_supported)); + } + for (const auto &handler : _handlers) { + if (handler->canWrite(file)) + return handler->writeFile(os, file); + } + return make_error("File type can't be written!", + std::make_error_code(std::errc::not_supported)); +} + +void Registry::addAllHandlers() { + addAllYAMLHandlers(); + addAllBinaryHandlers(); +} +void Registry::addAllYAMLHandlers() { + _handlers.emplace_back(std::unique_ptr(new ELFTextStubHandler)); +} +void Registry::addAllBinaryHandlers() { + _handlers.emplace_back(std::unique_ptr(new ELFObjHandler)); +} 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-elf-read.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi/basic-elf-read.test @@ -0,0 +1,63 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-tapi %t | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Address: 0x1000 + Content: "00000000000000000000000000000000" + Size: 16 + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Address: 0x2000 + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Address: 0x3000 +ProgramHeaders: + - Type: PT_LOAD + VAddr: 0x0000 + PAddr: 0x0000 + - Type: PT_DYNAMIC + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .dynamic +DynamicSymbols: + Global: + - Name: foo + Type: STT_FUNC + Size: 42 + Section: .dynsym + - Name: bar + Type: STT_OBJECT + Size: 42 + Section: .dynsym + - Name: baz + Type: STT_OBJECT + Section: .dynsym + - Name: not + Type: STT_OBJECT + - Name: nor + Type: STT_FUNC + Size: 10 + +#CHECK: --- !tapi-tbe-v1 +#CHECK-NEXT: SONAME: '' +#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/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/test/tools/llvm-tapi/read-soname.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi/read-soname.test @@ -0,0 +1,44 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-tapi %t | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_ALLOC ] + Address: 0x1000 # + Content: "0e000000000000000d0000000000000000000000000000000000000000000000" + Size: 32 + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Address: 0x2000 + EntSize: 24 + Size: 0 + Link: .dynstr + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Address: 0x3000 +# \0 b a z\0 n o t\0 b a r\0 s o m e l i b . s o\0 f o o\0 + Content: "0062617a006e6f740062617200736f6d656c69622e736f00666f6f00" +ProgramHeaders: + - Type: PT_LOAD + VAddr: 0x0000 + PAddr: 0x0000 + - Type: PT_DYNAMIC + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .dynamic + +#CHECK: --- !tapi-tbe-v1 +#CHECK-NEXT: SONAME: somelib.so +#CHECK-NEXT: ARCHITECTURE: X86_64 +#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,51 @@ +// a simple binary to test tapi, will evolve into the tapi command line utility. +#include +#include "llvm/TAPI/Registry.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: this is messy and not presentable at all. +int main(int argc, char *argv[]) { + Registry handler; + handler.addAllHandlers(); + 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,8 @@ +set(LLVM_LINK_COMPONENTS + TAPI +) + +add_llvm_unittest(TapiTests + YAMLTest.cpp + ELFTest.cpp +) Index: llvm/unittests/TAPI/ELFTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/TAPI/ELFTest.cpp @@ -0,0 +1,88 @@ +//===- llvm/unittests/TAPI/ELFTest.cpp ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TAPI/ELFStubFile.h" +#include "llvm/TAPI/ELFObjHandler.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::ELF; +static constexpr char RawDynStr[] = "\0somesym\0someobj.so\0anothersym\0"; + +TEST(ELFStubBuilderTest, ELFReadSONAME) { + tapi::ELFStub stub; + StringRef DynStr(RawDynStr); + + // initialize .dynamic table + Elf_Dyn_Impl DynArr[3]; + DynArr[0].d_tag = 0; + DynArr[0].d_un.d_val = 0; + DynArr[1].d_tag = DT_SONAME; + DynArr[1].d_un.d_val = 9; // index of someobj.so in RawDynStr + DynArr[2].d_tag = 0; + DynArr[2].d_un.d_val = 0; + ArrayRef> DynTab(DynArr, 3); + + // populate SONAME field + tapi::ELFStubBuilder builder(stub); + handleAllErrors(builder.populateSOName(DynTab, DynStr)); + + // check + EXPECT_STREQ("someobj.so", stub.so_name.c_str()); +} + +TEST(ELFStubBuilderTest, ELFReadMissingSONAME) { + tapi::ELFStub stub; + StringRef DynStr(RawDynStr); + + // initialize .dynamic table without SONAME entry + Elf_Dyn_Impl DynArr[2]; + DynArr[0].d_tag = 0; + DynArr[0].d_un.d_val = 0; + DynArr[1].d_tag = 0; + DynArr[1].d_un.d_val = 0; + ArrayRef> DynTab(DynArr, 2); + + // populate SONAME field + tapi::ELFStubBuilder builder(stub); + handleAllErrors(builder.populateSOName(DynTab, DynStr)); + + // check + EXPECT_STREQ("", stub.so_name.c_str()); +} + +TEST(ELFStubBuilderTest, ELFReadFuncSym) { + tapi::ELFStub stub; + StringRef DynStr(RawDynStr); + Elf_Sym_Impl someFunc; + someFunc.st_info = STT_FUNC; + someFunc.st_shndx = 1; // must be non-zero + someFunc.st_name = 1; // index of somesym in RawDynStr + someFunc.st_size = 48; // ignored + someFunc.st_other = 0; // ignored + someFunc.st_value = 0; // ignored + + tapi::ELFStubBuilder builder(stub); + auto SymOrErr = builder.buildSymbol(&someFunc, DynStr); + if (!SymOrErr) { + handleAllErrors(SymOrErr.takeError()); + } + + // check + EXPECT_STREQ(SymOrErr->st_name.c_str(), "somesym"); + EXPECT_EQ(SymOrErr->st_size, 0u); + EXPECT_EQ(SymOrErr->st_undefined, false); + EXPECT_EQ(SymOrErr->st_type, STT_FUNC); + EXPECT_STREQ(SymOrErr->st_warning.c_str(), ""); +} 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/ELFTextStubHandler.h" +#include "gtest/gtest.h" +#include + +using llvm::MemoryBuffer; +using llvm::tapi::ELFTextStubHandler; + +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); + ELFTextStubHandler handler; + + EXPECT_TRUE(handler.canRead(buf->getMemBufferRef())); + + auto file = handler.readFile(buf->getMemBufferRef()); + EXPECT_NE(nullptr, file.get()); +} + +TEST(LLVMTAPI, YAMLUnreadableTBE) { + ELFTextStubHandler 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) { + ELFTextStubHandler 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()); +}