Index: llvm/include/llvm/TAPI/ELFObjHandler.h =================================================================== --- /dev/null +++ llvm/include/llvm/TAPI/ELFObjHandler.h @@ -0,0 +1,226 @@ +//===- 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 populateDTNeeded(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; +}; + +template class ELFBinaryBuilder { +public: + typedef union Elf_Addr { + ELF::Elf32_Addr elf32; + ELF::Elf64_Addr elf64; + // TODO: test this + Elf_Addr(uint64_t word) { + if (ELFT::Is64Bits) + elf64 = word; + else + elf32 = word; + } + } Elf_Addr; + + typedef union Elf_Xword { + ELF::Elf32_Word elf32; + ELF::Elf64_Xword elf64; + // TODO: test this + Elf_Xword(uint64_t word) { + if (ELFT::Is64Bits) + elf64 = word; + else + elf32 = word; + } + } Elf_Xword; + + typedef union Elf_Sxword { + ELF::Elf32_Sword elf32; + ELF::Elf64_Sxword elf64; + // TODO: test this + Elf_Sxword(int64_t word) { + if (ELFT::Is64Bits) + elf64 = word; + else + elf32 = word; + } + } Elf_Sxword; + + using Elf_Ehdr = typename ELFT::Ehdr; + using Elf_Phdr = typename ELFT::Phdr; + using Elf_Shdr = typename ELFT::Shdr; + 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; + using Sh_Idx = unsigned int; + ELFBinaryBuilder() {} + + /// Static function to create a ELFBinaryBuilder from an ELFStub + static std::unique_ptr> fromELFStub( + const ELFStub &Stub); + + /// Serializes this ELFBinaryBuilder to a buffer. Sections are written in + /// the order they were added during the binary build. + Error serialize(std::vector &Buf); + + /// Sizes, offsets, indexes, links, etc. are determined in serialize(). + void initELFHeader(uint8_t os_abi, + uint8_t os_abi_ver, + uint16_t type, + uint16_t arch, + uint32_t flags); + + /// Sets program entry address. + void setEntry(Elf_Addr addr); + + /// Prepares section header table for adding sections. Call once before adding + /// sections via addSection() + Error initSectionHeaderTable(); + + /// Called once for each symbol table and string table pair. + /// Populates the symbol table with a STN_UNDEF, and string table with a null + /// character. Call initSymTab() on the symtab after it has been linked to + /// the respective strtab. + Error initSymTab(Sh_Idx Idx); + + /// Adds a section. All SH entries not available here are automatically set + /// during serialize or through other public functions. + void addSection(std::string name, + uint32_t type, + Elf_Xword flags, + Elf_Addr address, + uint32_t info, + Elf_Xword align, + Elf_Xword entsize); + bool linkSectionTo(std::string section, + const std::string linkedSection); + + uint32_t addProgramHeader(uint32_t type, + uint32_t flags, + Elf_Addr vaddr, + Elf_Addr paddr, + Elf_Xword file_size, + Elf_Xword mem_size, + Elf_Xword align); + + /// Groups sections by their flags to minimize PT_LOAD count. + void ptLoadSection(std::string name, uint32_t flags); + + /// Adds a dynamic entry to the specified section. + void addDynamicEntry(Sh_Idx Idx, Elf_Sxword tag, Elf_Xword val); + + /// Writes a buffer to a section, overwriting any existing contents. + bool writeToSection(Sh_Idx Idx, const void *Buf, int NumBytes, int Offset); + + /// Appends a buffer to a section. Assumes entries are padded appropriately. + int appendToSection(Sh_Idx Idx, const void *Buf, int NumBytes, int Align); + +private: + /// Only called during serialization; populates shstrndx with section names. + /// Warning: will clear the linked string table before populating. + Error initShdrStrTab(); + + /// Returns the offset in the buffer to which the data is written + int appendToBuffer(std::vector &Dest, + const void *Src, + Elf_Xword ElfSize, + Elf_Xword ElfAlign); + + /// Returns true if successful + bool writeToBuffer(std::vector &Dest, const void *Src, + Elf_Xword ElfSize, Elf_Xword ElfOffset); + + /// Turns a internal representation of a symbol into a binary ELFT symbol + Elf_Sym buildSymbol(const ELFSymbol &sym); + unsigned char convertTypeToInfo(ELFSymbolType type, uint8_t Binding); + /// Adds symbol Sym to section Idx's symbol table, marking the symbol as + /// defined in section DefIdx. + void addSymbolToSection(Sh_Idx Idx, ELFSymbol Sym, Sh_Idx DefIdx = 0); + + /// Serializes section to buffer, returning the offset that the section was + /// written to. + int serializeSectionToBuffer(std::vector &Dest, Sh_Idx Idx); + + // target + Elf_Ehdr _ehdr; + std::vector _shdrs; + std::vector _phdrs; + std::vector>> _section_data; + std::map _section_names; + std::map _offsets; + std::vector _shstrtab; +}; + +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(std::vector &Buf, + const ELFStub &Stub, + FileType OutputFormatb); + }; +}; + +} // 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,126 @@ +//===- 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; + bool st_weak; + std::string st_warning; +}; + +// TODO: inefficient, improve. +struct ELFStub { + std::map symbols; + std::vector needed; + 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) { + _arch = stub.arch; + _so_name = stub.so_name; + _needed = stub.needed; + 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; + stub.needed = _needed; + for (auto elem : _symbols) { + stub.symbols.insert(std::make_pair(elem.st_name, elem)); + } + return stub; + } + +private: + std::vector _symbols; + std::vector _needed; + 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,924 @@ +//===- 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 +#include + +using llvm::MemoryBufferRef; +using llvm::object::ELFObjectFile; + +using namespace llvm; +using namespace tapi; +using namespace object; +using namespace ELF; + +// Map keys for various sections +static constexpr char kEHDR[] = "EHDR"; +static constexpr char kSHDRS[] = "SHDRS"; +static constexpr char kPTLOAD[] = "PTLOAD"; +static constexpr char kPTDYN[] = "PTDYN"; + +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 DT_NEEDED list + if (Error Err = populateDTNeeded(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::populateDTNeeded(const Elf_Dyn_Range DynTab, + const StringRef DynStr) { + // Attempt to find DT_NEEDED entries in dynamic table. + for (auto i = DynTab.begin(); i < DynTab.end(); ++i) { + if (i->getTag() == ELF::DT_NEEDED) { + _stub.needed.push_back( + std::string(DynStr.data() + i->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()) { + + // TODO: is this right? + if (it->st_info >> 4 != STB_GLOBAL && it->st_info >> 4 != STB_WEAK) + continue; + if ((it->st_other & 0x3) != STV_DEFAULT && + (it->st_other & 0x3) != STV_PROTECTED) + continue; + 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? + if (rawSym->st_info >> 4 == STB_GLOBAL) + sym.st_weak = false; + else if (rawSym->st_info >> 4 == STB_WEAK) + sym.st_weak = true; + + // 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"; + } + + if ((rawSym->st_other & 0x3) == STV_PROTECTED) { + sym.st_warning = std::string("STV_PROTECTED"); + } + + // 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; +} + +template +std::unique_ptr> ELFBinaryBuilder::fromELFStub( + const ELFStub &Stub) { + std::unique_ptr builder(new ELFBinaryBuilder()); + builder->initELFHeader(/*os_abi=*/ ELFOSABI_NONE, + /*os_abi_ver=*/ 0u, + ET_DYN, + Stub.arch, + /*flags=*/ 0u); + if (builder->initSectionHeaderTable()) + return nullptr; + + // Note: Order matters slightly; the order sections are added in is the same + // order that the sections will be serialized in. This is helpful for + // optimizing alignment. + auto AlignSize = sizeof(ELFT::Is64Bits ? Elf_Xword::elf64 : Elf_Xword::elf32); + + builder->addSection(".dynsym", + SHT_DYNSYM, + SHF_ALLOC, // TODO: should be correct + /*address=*/ 0x1000, // TODO: ??? + /*info=*/ 1, // TODO: should be correct + /*align=*/ AlignSize, // TODO: should be correct + /*entsize=*/sizeof(Elf_Sym)); // TODO: should be correct + + builder->addSection(".dynstr", + SHT_STRTAB, + SHF_ALLOC, + /*address=*/ 0x2000, // TODO: ??? + /*info=*/ 0, + /*align=*/ 1, + /*entsize=*/ 0); + + builder->addSection(".def", + SHT_NOBITS, + SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR, + /*address=*/ 0x3000, // TODO: ??? + /*info=*/ 0, + /*align=*/ 8, // TODO: MAX align + /*entsize=*/0); + if (!Stub.so_name.empty() || !Stub.needed.empty()) { + builder->addSection(".dynamic", + SHT_DYNAMIC, + SHF_WRITE | SHF_ALLOC, + /*address=*/ 0x4000, // TODO: ??? + /*info=*/ 0, + /*align=*/ AlignSize, + /*entsize=*/ sizeof(Elf_Dyn));// TODO: should be correct + } + + builder->addSection(".shstrtab", + SHT_STRTAB, + /*flags=*/ 0, + /*address=*/ 0, + /*info=*/ 0, + /*align=*/ 1, + /*entsize=*/ 0); + + int SHStrTabIdx = builder->_section_names[".shstrtab"]; + + // link SHT_NULL to .shstrtab + builder->_shdrs[0].sh_link = SHStrTabIdx; + + // link ELF header to .shstrtab + builder->_ehdr.e_shstrndx = SHStrTabIdx; + + int DefIdx = builder->_section_names[".def"]; + int DynSymIdx = builder->_section_names[".dynsym"]; + int DynStrIdx = builder->_section_names[".dynstr"]; + + builder->linkSectionTo(".dynsym", ".dynstr"); + if (builder->_section_names.count(".dynamic") == 1) { + builder->linkSectionTo(".dynamic", ".dynstr"); + } + + // This initializes .dynstr as well. + if (builder->initSymTab(DynSymIdx)) + return nullptr; + + // Write soname to .dynstr and add dynamic entry + if (!Stub.so_name.empty()) { + int DynIdx = builder->_section_names.at(".dynamic"); + int SoNameOffset = builder->appendToSection(DynStrIdx, + Stub.so_name.data(), + Stub.so_name.size()+ 1, + /*align=*/0); + + builder->addDynamicEntry(DynIdx, DT_SONAME, SoNameOffset); + } + + // Write needed to .dynstr and add dynamic entries + if (!Stub.needed.empty()) { + int DynIdx = builder->_section_names.at(".dynamic"); + for (StringRef Needed : Stub.needed) { + int DTNOffset = builder->appendToSection(DynStrIdx, + Needed.data(), + Needed.size()+ 1, + /*align=*/0); + builder->addDynamicEntry(DynIdx, DT_NEEDED, DTNOffset); + } + } + + // Write null dynamic entry if there were dynamic entries. + if (builder->_section_names.count(".dynamic") == 1) { + int DynIdx = builder->_section_names.at(".dynamic"); + builder->addDynamicEntry(DynIdx, DT_NULL, 0); + } + + for (auto SymEnt : Stub.symbols) { + builder->addSymbolToSection(DynSymIdx, SymEnt.second, DefIdx); + } + return std::move(builder); +} + +template +Error ELFBinaryBuilder::initSymTab(Sh_Idx Idx) { + if (Idx >= _shdrs.size()) + return make_error("Section header index out of range.", + std::make_error_code(std::errc::invalid_argument)); + + Sh_Idx StrTabIdx = _shdrs[Idx].sh_link; + if (Idx == 0) + return make_error("Symbol table not linked to a string table.", + std::make_error_code(std::errc::invalid_argument)); + + if (_section_data[StrTabIdx]->size() == 0) + _section_data[StrTabIdx]->push_back('\0'); + + // write STN_UNDEF in symbol table + auto SymAlign = sizeof(ELFT::Is64Bits ? Elf_Xword::elf64 : Elf_Xword::elf32); + Elf_Sym UndefSym; + memset(&UndefSym, 0, sizeof(Elf_Sym)); + appendToSection(Idx, + &UndefSym, + sizeof(Elf_Sym), + SymAlign); + + return Error::success(); +} + +template +void ELFBinaryBuilder::addSymbolToSection(Sh_Idx Idx, ELFSymbol Sym, + Sh_Idx DefIdx) { + // TODO: This function's implementation is slightly too specialized. + assert(Idx < _shdrs.size() && "Section header index out of range."); + Sh_Idx StrTabIdx = _shdrs[Idx].sh_link; + assert(StrTabIdx != 0 && "Symbol table not linked to a string table."); + auto SymAlign = sizeof(ELFT::Is64Bits ? Elf_Xword::elf64 : Elf_Xword::elf32); + + // Build binary symbol + Elf_Sym SymData; + SymData.st_name = appendToSection(StrTabIdx, + Sym.st_name.data(), + Sym.st_name.length() + 1, + /*ElfAlign=*/1); + uint8_t Binding = Sym.st_weak ? STB_WEAK : STB_GLOBAL; + SymData.st_info = convertTypeToInfo(Sym.st_type, Binding); + SymData.st_other = STV_DEFAULT; + SymData.st_shndx = Sym.st_undefined ? SHN_UNDEF : DefIdx; + SymData.st_size = Sym.st_size; + + _shdrs[Idx].sh_size += sizeof(Elf_Sym); + + // TODO: only do the following if defined? + // TODO: Don't limit to SHT_NOBITS. Augment to support explicit offset in real + // section. + assert(_shdrs[DefIdx].sh_type == SHT_NOBITS && + "Symbol defined in a section that is not type SHT_NOBITS."); + + // determine offset in the section this symbol is defined in + int Align = sizeof(ELFT::Is64Bits ? Elf_Addr::elf64 : Elf_Addr::elf32); + int Offset = _shdrs[DefIdx].sh_size; + if (Align == 0) + Align = 1; + if (Offset % Align != 0) { + int Pad = Align - (Offset % Align); + Offset += Pad; + } + SymData.st_value = Offset; + + // update section size + _shdrs[DefIdx].sh_size = Offset + Sym.st_size; + + appendToSection(Idx, + &SymData, + sizeof(Elf_Sym), + SymAlign); +} + +template +unsigned char ELFBinaryBuilder::convertTypeToInfo(ELFSymbolType Type, + uint8_t Binding) { + uint8_t binding = Binding << 4; + uint8_t info = 0; + switch (Type) { + case ELFSymbolType::no_type: + info = ELF::STT_NOTYPE; + break; + case ELFSymbolType::object: + info = ELF::STT_OBJECT; + break; + case ELFSymbolType::function: + info = ELF::STT_FUNC; + break; + case ELFSymbolType::tls: + info = ELF::STT_TLS; + break; + default: + info = ELF::STT_NOTYPE; + } + return info | binding; +} +template +void ELFBinaryBuilder::initELFHeader(uint8_t os_abi, + uint8_t os_abi_ver, + uint16_t type, + uint16_t arch, + uint32_t flags) { + memset(&_ehdr, 0, sizeof(Elf_Ehdr)); + // ELF identification + _ehdr.e_ident[EI_MAG0] = 0x7f; //ELFMAG0 + _ehdr.e_ident[EI_MAG1] = 'E'; //ELFMAG1 + _ehdr.e_ident[EI_MAG2] = 'L'; //ELFMAG2 + _ehdr.e_ident[EI_MAG3] = 'F'; //ELFMAG3 + _ehdr.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; + bool IsLittleEndian = ELFT::TargetEndianness == support::little; + _ehdr.e_ident[EI_DATA] = IsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB; + _ehdr.e_ident[EI_VERSION] = EV_CURRENT; + _ehdr.e_ident[EI_OSABI] = os_abi; + _ehdr.e_ident[EI_ABIVERSION] = os_abi_ver; + + // remainder of ELF header + _ehdr.e_type = type; + _ehdr.e_machine = arch; + _ehdr.e_version = EV_CURRENT; + _ehdr.e_entry = 0x00; + _ehdr.e_flags = flags; + _ehdr.e_ehsize = sizeof(Elf_Ehdr); + + // These are all zero initialized, will be overwritten in later stages. + _ehdr.e_phoff = 0; // TODO: update at end + _ehdr.e_shoff = 0; + _ehdr.e_phentsize = 0; // TODO: update at end + _ehdr.e_phnum = 0; // TODO: update at end + _ehdr.e_shentsize = 0; + _ehdr.e_shnum = 0; + _ehdr.e_shstrndx = 0; +} + +template +void ELFBinaryBuilder::setEntry(Elf_Addr addr) { + _ehdr.e_entry = ELFT::Is64Bits ? addr.elf64 : addr.elf32; +} + + +template +Error ELFBinaryBuilder::initSectionHeaderTable() { + if (_shdrs.size() > 0) + return make_error( + "Can't initialize section header that already has data.", + std::make_error_code(std::errc::not_supported)); + + Elf_Shdr ShUndef; + memset(&ShUndef, 0, sizeof(Elf_Shdr)); + _ehdr.e_shnum = _ehdr.e_shnum + 1; + _shdrs.push_back(ShUndef); + + // dummy entry for section data to make access easier + _section_data.emplace_back(make_unique>()); + + return Error::success(); +} + +template +void ELFBinaryBuilder::addSection(std::string name, + uint32_t type, + Elf_Xword flags, + Elf_Addr address, + uint32_t info, + Elf_Xword align, + Elf_Xword entsize) { + if (_section_names.count(name) != 0) + return; + + Elf_Shdr Shdr; + Shdr.sh_type = type; + Shdr.sh_flags = ELFT::Is64Bits ? flags.elf64 : flags.elf32; + Shdr.sh_addr = ELFT::Is64Bits ? address.elf64 : flags.elf32; + Shdr.sh_info = info; + Shdr.sh_addralign = ELFT::Is64Bits ? align.elf64 : align.elf32; + Shdr.sh_entsize = ELFT::Is64Bits ? entsize.elf64 : entsize.elf32; + + // These are populated/managed elsewhere, for now just initialize. + Shdr.sh_name = 0; + Shdr.sh_offset = 0; + Shdr.sh_size = 0; + Shdr.sh_link = 0; + + _ehdr.e_shnum = _ehdr.e_shnum + 1; + _section_names.emplace(name, _shdrs.size()); + _section_data.emplace_back(make_unique>()); + _shdrs.push_back(Shdr); +} + +template +bool ELFBinaryBuilder::linkSectionTo(std::string section, + const std::string linkedSection) { + auto lhs = _section_names.find(section); + auto rhs = _section_names.find(linkedSection); + if (lhs == _section_names.end() || rhs == _section_names.end()) + return false; + _shdrs.at(lhs->second).sh_link = rhs->second; + return true; +} + +template +void ELFBinaryBuilder::addDynamicEntry(Sh_Idx Idx, Elf_Sxword tag, + Elf_Xword val) { + assert(_shdrs[Idx].sh_type == SHT_DYNAMIC && + "Failed adding dynamic entry to non-dynamic section"); + + int Align = sizeof(ELFT::Is64Bits ? Elf_Addr::elf64 : Elf_Addr::elf32); + + Elf_Dyn Dyn; + Dyn.d_tag = ELFT::Is64Bits ? tag.elf64 : tag.elf32; + // In theory, setting to d_val or d_ptr should behave identically. + Dyn.d_un.d_val = ELFT::Is64Bits ? val.elf64 : val.elf32; + + appendToSection(Idx, &Dyn, sizeof(Elf_Dyn), Align); +} + +template +uint32_t ELFBinaryBuilder::addProgramHeader(uint32_t type, + uint32_t flags, + Elf_Addr vaddr, + Elf_Addr paddr, + Elf_Xword file_size, + Elf_Xword mem_size, + Elf_Xword align) { + Elf_Phdr Phdr; + Phdr.p_type = type; + Phdr.p_flags = flags; + // TODO: make macro that handles +#define ELFT_CASE(MEMBER) ELFT::Is64Bits ? MEMBER.elf64 : MEMBER.elf32 + Phdr.p_vaddr = ELFT_CASE(vaddr); + Phdr.p_paddr = ELFT_CASE(paddr); + Phdr.p_filesz = ELFT_CASE(file_size); + Phdr.p_memsz = ELFT_CASE(mem_size); + Phdr.p_align = align; + Phdr.p_offset = 0; + _phdrs.push_back(Phdr); +#undef ELFT_CASE + return _ehdr->e_phsize++; +} + +template +Error ELFBinaryBuilder::initShdrStrTab() { + // Write section names to .shstrtab and update name offsets + if (_ehdr.e_shstrndx == 0u) { + return make_error( + "Section header string table unspecified exist!", + std::make_error_code(std::errc::not_supported)); + } + // initialize .shstrtab data + int ShdrStrTabIdx = _ehdr.e_shstrndx; + _section_data[ShdrStrTabIdx]->clear(); + _section_data[ShdrStrTabIdx]->push_back('\0'); + + // populate .shstrtab with section names + for (auto elem : _section_names) { + std::string name = elem.first; + int ShdrIdx = elem.second; + // write name to section header string table + int NameOffset = appendToSection(ShdrStrTabIdx, + name.c_str(), + name.length() + 1, + /*ElfAlign=*/1); + _shdrs[ShdrIdx].sh_name = NameOffset; + } + + // update sh_strtab size in ehdr. + _shdrs[ShdrStrTabIdx].sh_size = _section_data[ShdrStrTabIdx]->size(); + return Error::success(); +} + +template +int ELFBinaryBuilder::appendToSection(Sh_Idx Idx, const void *Buf, + int NumBytes, int Align) { + assert(Idx < _shdrs.size() && "Section header index out of range."); + return appendToBuffer(*_section_data[Idx], Buf, NumBytes, Align); +} + +template +bool ELFBinaryBuilder::writeToBuffer(std::vector &Dest, + const void *Src, + Elf_Xword ElfSize, + Elf_Xword ElfOffset) { + auto NumBytes = ELFT::Is64Bits ? ElfSize.elf64 : ElfSize.elf32; + auto Offset = ELFT::Is64Bits ? ElfOffset.elf64 : ElfOffset.elf32; + if (Dest.size() < Offset + NumBytes) + return false; + + std::memcpy(Dest.data() + Offset, Src, NumBytes); + return true; +} + +template +int ELFBinaryBuilder::appendToBuffer(std::vector &Dest, + const void *Src, + Elf_Xword ElfSize, + Elf_Xword ElfAlign) { + auto NumBytes = ELFT::Is64Bits ? ElfSize.elf64 : ElfSize.elf32; + auto Align = ELFT::Is64Bits ? ElfAlign.elf64 : ElfAlign.elf32; + if (NumBytes == 0) + return 0; + else if (Dest.capacity() == 0) + Dest.reserve(1); + + int PrevSize = Dest.size(); + if (Align == 0) + Align = 1; + auto Pad = Align - (PrevSize % Align); + if (Pad == Align) + Pad = 0; + + while (Dest.capacity() < Dest.size() + Pad + NumBytes) { + Dest.reserve(Dest.capacity()*2); + } + + // add padding + if (Pad != 0) { + Dest.resize(PrevSize + Pad); + memset(Dest.data() + PrevSize, 0, Pad); + PrevSize = Dest.size(); + } + // append data + Dest.resize(PrevSize + NumBytes); + std::memcpy(Dest.data() + PrevSize, Src, NumBytes); + + return PrevSize; +} + +template +Error ELFBinaryBuilder::serialize(std::vector &Buf) { + if (Buf.size() > 0) + return make_error("Buffer is not empty!", + std::make_error_code(std::errc::invalid_argument)); + _offsets.clear(); + + // ELF Header + appendToBuffer(Buf, &_ehdr, sizeof(Elf_Ehdr), /*ElfAlign=*/1); + _offsets[kEHDR] = 0; + + // Initialization of section headers. Note that having no section headers is + // very possible: an ELF file might only have program headers. + int ShdrSize = sizeof(Elf_Shdr) * _shdrs.size(); + if (ShdrSize != 0) { + if (_shdrs[0].sh_type != SHT_NULL) { + return make_error( + "Initial section header is not type SHT_NULL.", + std::make_error_code(std::errc::not_supported)); + } + _ehdr.e_shnum = _shdrs.size(); + _ehdr.e_shentsize = sizeof(Elf_Shdr); + _ehdr.e_shoff = Buf.size(); + + // update entries in SHT_NULL + _shdrs[0].sh_size = _ehdr.e_shnum; + _shdrs[0].sh_link = _ehdr.e_shstrndx; + + // populate section names + if (Error Err = initShdrStrTab()) + return Err; + } + + // reserve program headers + auto PhdrAlign = sizeof(ELFT::Is64Bits ? Elf_Xword::elf64 : Elf_Xword::elf32); + Elf_Phdr PTLoadHdr; + _offsets[kPTLOAD] = + appendToBuffer(Buf, &PTLoadHdr, sizeof(Elf_Phdr), PhdrAlign); + // populate pheader related info to fill in later. + PTLoadHdr.p_type = PT_LOAD; + PTLoadHdr.p_flags = PF_R; + PTLoadHdr.p_offset = 0; + PTLoadHdr.p_vaddr = 0x1000; // temporary, make machine specific later + PTLoadHdr.p_paddr = 0x1000; // temporary, make machine specific later + PTLoadHdr.p_filesz = 0; // done later + PTLoadHdr.p_memsz = 0; // done later + PTLoadHdr.p_align = PhdrAlign; // temporary, figure out right value + _ehdr.e_phnum += 1; + _ehdr.e_phentsize = sizeof(Elf_Phdr); + _ehdr.e_phoff = _offsets[kPTLOAD]; + + Elf_Phdr PTDynPhdr; + for (unsigned int Idx = 0u; Idx < _shdrs.size(); ++Idx) { + if (_shdrs[Idx].sh_type == SHT_DYNAMIC) { + _offsets[kPTDYN] = + appendToBuffer(Buf, &PTDynPhdr, sizeof(Elf_Phdr), PhdrAlign); + _ehdr.e_phnum += 1; + } + } + + // write section headers + auto ShdrAlign = sizeof(ELFT::Is64Bits ? Elf_Xword::elf64 : Elf_Xword::elf32); + _offsets[kSHDRS] = appendToBuffer(Buf, _shdrs.data(), ShdrSize, ShdrAlign); + // update position of section headers in ELF header. + _ehdr.e_shoff = _offsets[kSHDRS]; + + // Sections + for (unsigned int Idx = 0u; Idx < _shdrs.size(); ++Idx) { + // Ignore SHT_NULL + if (_shdrs[Idx].sh_type == SHT_NULL) { + continue; + } + + int SectionOffset = serializeSectionToBuffer(Buf, Idx); + _shdrs[Idx].sh_offset = SectionOffset; + + // SHT_NOBITS pre-calculates size, so don't set here. + if (_shdrs[Idx].sh_type != SHT_NOBITS) + _shdrs[Idx].sh_size = _section_data[Idx]->size(); + + if (_shdrs[Idx].sh_type == SHT_DYNAMIC) { + PTDynPhdr.p_type = PT_DYNAMIC; + PTDynPhdr.p_flags = PF_W | PF_R; + PTDynPhdr.p_offset = SectionOffset; + PTDynPhdr.p_vaddr = 0x2000; // temporary, make machine specific later + PTDynPhdr.p_paddr = 0x2000; // temporary, make machine specific later + PTDynPhdr.p_filesz = _shdrs[Idx].sh_size; + PTDynPhdr.p_memsz = _shdrs[Idx].sh_size; + PTDynPhdr.p_align = PhdrAlign; // temporary, figure out right value + } else if (_shdrs[Idx].sh_type == SHT_NOBITS) { + // TODO: include size in memsize. + PTLoadHdr.p_memsz = _shdrs[Idx].sh_size; + } else if (Idx == _ehdr.e_shstrndx) { + // TODO: set pt_load file size to offset. + PTLoadHdr.p_filesz = SectionOffset; + + // TODO: set pt_load mem size to offset, max alignto then add to memsize. + int MemSize = SectionOffset; + int Align = 0x1000; + if (MemSize % Align != 0) { + int Pad = Align - (MemSize % Align); + MemSize += Pad; + } + MemSize += PTLoadHdr.p_memsz; + PTLoadHdr.p_memsz = MemSize; + } + } + + // Program Headers + // TODO: update program headers + writeToBuffer(Buf, &PTLoadHdr, sizeof(Elf_Phdr), _offsets[kPTLOAD]); + if (_offsets[kPTDYN] != 0) + writeToBuffer(Buf, &PTDynPhdr, sizeof(Elf_Phdr), _offsets[kPTDYN]); + + // update any changes that were made to the ELF header + writeToBuffer(Buf, &_ehdr, sizeof(Elf_Ehdr), /*ElfOffset=*/0); + // update any changes that were made to the section headers + writeToBuffer(Buf, + _shdrs.data(), + _shdrs.size() * sizeof(Elf_Shdr), + _offsets[kSHDRS]); + + return Error::success(); +} + + +template +int ELFBinaryBuilder::serializeSectionToBuffer(std::vector &Dest, + Sh_Idx Idx) { + assert(Idx < _shdrs.size() && "Section header index out of range."); + if (_shdrs[Idx].sh_type == SHT_NOBITS) + return Dest.size(); + + return appendToBuffer(Dest, + _section_data[Idx]->data(), + _section_data[Idx]->size(), + (int) _shdrs[Idx].sh_addralign); +} + +llvm::Error ELFObjHandler::BinaryELFWriter::write(std::vector &Buf, + const ELFStub &Stub, + FileType OutputFormat) { + if (OutputFormat == FileType::ELF_32LE) { + auto builder = ELFBinaryBuilder::fromELFStub(Stub); + if (auto Err = builder->serialize(Buf)) + return Err; + } else if (OutputFormat == FileType::ELF_32BE) { + auto builder = ELFBinaryBuilder::fromELFStub(Stub); + if (auto Err = builder->serialize(Buf)) + return Err; + } else if (OutputFormat == FileType::ELF_64LE) { + auto builder = ELFBinaryBuilder::fromELFStub(Stub); + if (auto Err = builder->serialize(Buf)) + return Err; + } else if (OutputFormat == FileType::ELF_64BE) { + auto builder = ELFBinaryBuilder::fromELFStub(Stub); + if (auto Err = builder->serialize(Buf)) + return Err; + } + return Error::success(); +} + + +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 { + if (SUPPORTED_FILE_TYPES & 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) { + 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)); + } + ELFStub Stub = inFile->getStub(); + std::vector Buf; + FileType Format = inFile->getFileType(); + + auto Err = BinaryELFWriter::write(Buf, Stub, Format); + if (Err) { + // TODO: can you just toString an Error? + llvm::errs() << "Failed reading ELF file: " << Err << "\n"; + return Err; + } + + os.write(Buf.data(), Buf.size()); + return Error::success(); +} Index: llvm/lib/TAPI/ELFTextStubHandler.cpp =================================================================== --- /dev/null +++ llvm/lib/TAPI/ELFTextStubHandler.cpp @@ -0,0 +1,180 @@ +//===- 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("")); + io.mapOptional("WEAK", symbol.st_weak, false); + + // 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 SequenceTraits> { + static size_t size(IO &io, std::vector &list) { + return list.size(); + } + static std::string &element(IO &io, std::vector &list, size_t index) { + if (index >= list.size()) + list.resize(index + 1); + return list[index]; + } + + // make inline + static const bool flow = true; +}; + +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); + io.mapOptional("NEEDED", stub.needed); + 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 -output-format=TBE %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 -output-format=TBE %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,45 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-tapi -output-format=TBE %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: SYMBOLS: +#CHECK-NEXT: ... Index: llvm/test/tools/llvm-tapi/tbe-elf-tbe.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi/tbe-elf-tbe.test @@ -0,0 +1,24 @@ +# RUN: llvm-tapi -output-format=ELF64LE %s > %t +# RUN: llvm-tapi -output-format=TBE %t | 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/tbe-no-symbols.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi/tbe-no-symbols.test @@ -0,0 +1,12 @@ +# RUN: llvm-tapi -output-format=TBE %s | FileCheck %s + +--- !tapi-tbe-v1 +SONAME: somelib.so +ARCHITECTURE: X86_64 +... + +#CHECK: --- !tapi-tbe-v1 +#CHECK-NEXT: SONAME: somelib.so +#CHECK-NEXT: ARCHITECTURE: X86_64 +#CHECK-NEXT: SYMBOLS: +#CHECK-NEXT: ... Index: llvm/test/tools/llvm-tapi/write-ehdr-32be.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi/write-ehdr-32be.test @@ -0,0 +1,21 @@ +# RUN: llvm-tapi -output-format=ELF32BE %s > %t +# RUN: llvm-readobj -file-headers %t | FileCheck %s + +--- !tapi-tbe-v1 +SONAME: somelib.so +ARCHITECTURE: X86_64 +... + +#CHECK: Format: ELF32-x86-64 +#CHECK-NEXT: Arch: x86_64 +#CHECK-NEXT: AddressSize: 32bit +#CHECK: Ident { +#CHECK-NEXT: Magic: (7F 45 4C 46) +#CHECK-NEXT: Class: 32-bit (0x1) +#CHECK-NEXT: DataEncoding: BigEndian (0x2) +#CHECK-NEXT: FileVersion: 1 +#CHECK-NEXT: OS/ABI: SystemV (0x0) +#CHECK-NEXT: ABIVersion: 0 +#CHECK: Type: SharedObject (0x3) +#CHECK-NEXT: Machine: EM_X86_64 (0x3E) +#CHECK: HeaderSize: 52 Index: llvm/test/tools/llvm-tapi/write-ehdr-64le.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi/write-ehdr-64le.test @@ -0,0 +1,21 @@ +# RUN: llvm-tapi -output-format=ELF64LE %s > %t +# RUN: llvm-readobj -file-headers %t | FileCheck %s + +--- !tapi-tbe-v1 +SONAME: somelib.so +ARCHITECTURE: X86_64 +... + +#CHECK: Format: ELF64-x86-64 +#CHECK-NEXT: Arch: x86_64 +#CHECK-NEXT: AddressSize: 64bit +#CHECK: Ident { +#CHECK-NEXT: Magic: (7F 45 4C 46) +#CHECK-NEXT: Class: 64-bit (0x2) +#CHECK-NEXT: DataEncoding: LittleEndian (0x1) +#CHECK-NEXT: FileVersion: 1 +#CHECK-NEXT: OS/ABI: SystemV (0x0) +#CHECK-NEXT: ABIVersion: 0 +#CHECK: Type: SharedObject (0x3) +#CHECK-NEXT: Machine: EM_X86_64 (0x3E) +#CHECK: HeaderSize: 64 Index: llvm/test/tools/llvm-tapi/write-syms-64le.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-tapi/write-syms-64le.test @@ -0,0 +1,62 @@ +# RUN: llvm-tapi -output-format=ELF64LE %s > %t +# RUN: llvm-readobj -dyn-symbols %t | FileCheck %s + +--- !tapi-tbe-v1 +SONAME: somelib.so +ARCHITECTURE: X86_64 +SYMBOLS: + bar: { TYPE: OBJECT, SIZE: 42 } + baz: { TYPE: OBJECT, SIZE: 8 } + foo: { TYPE: FUNCTION } + nor: { TYPE: FUNCTION, UNDEFINED: true } + not: { TYPE: OBJECT, UNDEFINED: true } +... + +#CHECK: Name: @ (0) +#CHECK-NEXT: Value: 0x0 +#CHECK-NEXT: Size: 0 +#CHECK-NEXT: Binding: Local (0x0) +#CHECK-NEXT: Type: None (0x0) +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: Undefined (0x0) + +#CHECK: Name: bar@ (12) +#CHECK-NEXT: Value: 0x0 +#CHECK-NEXT: Size: 42 +#CHECK-NEXT: Binding: Global (0x1) +#CHECK-NEXT: Type: Object (0x1) +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: .def (0x3) + +#CHECK: Name: baz@ (16) +#CHECK-NEXT: Value: 0x30 +#CHECK-NEXT: Size: 8 +#CHECK-NEXT: Binding: Global (0x1) +#CHECK-NEXT: Type: Object (0x1) +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: .def (0x3) + +#CHECK: Name: foo@ (20) +#CHECK-NEXT: Value: 0x38 +#CHECK-NEXT: Size: 0 +#CHECK-NEXT: Binding: Global (0x1) +#CHECK-NEXT: Type: Function (0x2) +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: .def (0x3) + +#CHECK: Name: nor@ (24) +#CHECK-NEXT: Value: 0x38 +#CHECK-NEXT: Size: 0 +#CHECK-NEXT: Binding: Global (0x1) +#CHECK-NEXT: Type: Function (0x2) +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: Undefined (0x0) + + +#CHECK: Name: not@ (28) +#CHECK-NEXT: Value: 0x38 +#CHECK-NEXT: Size: 0 +#CHECK-NEXT: Binding: Global (0x1) +#CHECK-NEXT: Type: Object (0x1) +#CHECK-NEXT: Other: 0 +#CHECK-NEXT: Section: Undefined (0x0) 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,71 @@ +// 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 < 3) { + llvm::outs() << "too few arguments\n"; + return 1; + } + StringRef FormatFlag = argv[1]; + StringRef InFile = argv[2]; + if (!FormatFlag.startswith("-output-format=")) { + llvm::outs() << "Missing or invalid output format flag!"; + return 1; + } + FileType OutputFormat; + if (FormatFlag.endswith("ELF32LE")) + OutputFormat = FileType::ELF_32LE; + else if (FormatFlag.endswith("ELF32BE")) + OutputFormat = FileType::ELF_32BE; + else if (FormatFlag.endswith("ELF64LE")) + OutputFormat = FileType::ELF_64LE; + else if (FormatFlag.endswith("ELF64BE")) + OutputFormat = FileType::ELF_64BE; + else if (FormatFlag.endswith("TBE")) + OutputFormat = FileType::TBE_V1; + else { + llvm::outs() << "Unsupported output format!"; + return 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(OutputFormat)) { + 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()); +}