Index: llvm/test/CMakeLists.txt =================================================================== --- llvm/test/CMakeLists.txt +++ llvm/test/CMakeLists.txt @@ -61,6 +61,7 @@ dsymutil llvm-dwarfdump llvm-dwp + llvm-elfabi llvm-exegesis llvm-extract llvm-isel-fuzzer Index: llvm/test/tools/llvm-elfabi/binary-read-arch.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-elfabi/binary-read-arch.test @@ -0,0 +1,17 @@ +# RUN: yaml2obj %s > %t.abi +# RUN: llvm-elfabi %t.abi -emit-tbe=%t2 +# RUN: cat %t2 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 + +#CHECK: --- !tapi-tbe +#CHECK-NEXT: TbeVersion: {{[1-9]\d*\.(0|([1-9]\d*))}} +#CHECK-NEXT: SoName: binary-read-arch.test.so +#CHECK-NEXT: Arch: x86_64 +#CHECK-NEXT: Symbols: {} +#CHECK-NEXT: ... Index: llvm/test/tools/llvm-elfabi/replace-soname-tbe.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-elfabi/replace-soname-tbe.test @@ -0,0 +1,17 @@ +# RUN: yaml2obj %s > %t +# RUN: llvm-elfabi %t --emit-tbe=%t2 -so-name=best.so +# RUN: cat %t2 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_AARCH64 + +#CHECK: --- !tapi-tbe +#CHECK-NEXT: TbeVersion: {{[1-9]\d*\.(0|([1-9]\d*))}} +#CHECK-NEXT: SoName: best.so +#CHECK-NEXT: Arch: AArch64 +#CHECK-NEXT: Symbols: {} +#CHECK-NEXT: ... Index: llvm/test/tools/llvm-elfabi/tbe-read-basic.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-elfabi/tbe-read-basic.test @@ -0,0 +1,26 @@ +# RUN: llvm-elfabi %s -emit-tbe=%t +# RUN: cat %t | FileCheck %s + +--- !tapi-tbe +SoName: somelib.so +TbeVersion: 1.0 +Arch: x86_64 +Symbols: + foo: { Type: Func } + bar: { Type: Object, Size: 42 } + baz: { Type: Object, Size: 8 } + not: { Type: Object, Undefined: true, Size: 128 } + nor: { Type: Func, Undefined: true } +... + +#CHECK: --- !tapi-tbe +#CHECK-NEXT: TbeVersion: {{[1-9]\d*\.(0|([1-9]\d*))}} +#CHECK-NEXT: SoName: somelib.so +#CHECK-NEXT: Arch: x86_64 +#CHECK-NEXT: Symbols: +#CHECK-NEXT: bar: { Type: Object, Size: 42 } +#CHECK-NEXT: baz: { Type: Object, Size: 8 } +#CHECK-NEXT: foo: { Type: Func } +#CHECK-NEXT: nor: { Type: Func, Undefined: true } +#CHECK-NEXT: not: { Type: Object, Size: 128, Undefined: true } +#CHECK-NEXT: ... Index: llvm/tools/LLVMBuild.txt =================================================================== --- llvm/tools/LLVMBuild.txt +++ llvm/tools/LLVMBuild.txt @@ -32,6 +32,7 @@ llvm-dis llvm-dwarfdump llvm-dwp + llvm-elfabi llvm-exegesis llvm-extract llvm-jitlistener Index: llvm/tools/llvm-elfabi/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-elfabi/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Object + Support + TextAPI + ) + +add_llvm_tool(llvm-elfabi + ELFObjHandler.cpp + llvm-elfabi.cpp + ) Index: llvm/tools/llvm-elfabi/ELFObjHandler.h =================================================================== --- /dev/null +++ llvm/tools/llvm-elfabi/ELFObjHandler.h @@ -0,0 +1,65 @@ +//===- ELFObjHandler.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ +// +// This supports reading and writing of elf dynamic shared objects. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H +#define LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H + +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/TextAPI/ELF/ELFStub.h" + +namespace llvm { + +class MemoryBuffer; + +namespace elfabi { + +/// This class provides an interface for populating ELFStub objects from raw ELF +/// binaries. build() streamlines the process of populating a ELFStub by pulling +/// the needed section/header pointers from `Bin`. Functions are then called +/// to appropriately populate all members of an ELFStub object. +/// +/// While build() requires `Bin` to be non-null, the other functions can be +/// called by directly providing the needed information. +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 &EmptyStub) + : Stub(EmptyStub), Bin(ElfObj) {} + ELFStubBuilder(ELFStub &EmptyStub) : Stub(EmptyStub), Bin(nullptr) {} + + /// Populate all members of an ELFStub from an ELFObjectFile. + void build(); + + /// Populate the `Arch` member of an ELFStub. + /// @param Header Pointer to an ELF file header. + void populateArch(const Elf_Ehdr *Header); + +private: + ELFStub &Stub; + const object::ELFObjectFile *Bin; +}; + +class ELFObjReader { +public: + std::unique_ptr readFile(MemoryBufferRef Buf); +}; + +} // end namespace elfabi +} // end namespace llvm + +#endif // LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H Index: llvm/tools/llvm-elfabi/ELFObjHandler.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-elfabi/ELFObjHandler.cpp @@ -0,0 +1,75 @@ +//===- 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 "ELFObjHandler.h" +#include "llvm-elfabi.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TextAPI/ELF/ELFStub.h" + +using llvm::MemoryBufferRef; +using llvm::object::ELFObjectFile; + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::elfabi; +using namespace llvm::ELF; + +template void ELFStubBuilder::build() { + if (Bin == nullptr) + terminateWithError( + createStringError(errc::invalid_argument, + "A stub can't be built from a NULL source binary!")); + const ELFFile *ElfFile = Bin->getELFFile(); + + // Fetch needed tables/headers. This will eventually include .dynamic, + // .dynsym, and .dynstr. + const Elf_Ehdr *ElfHeader = ElfFile->getHeader(); + + // populate architecture + populateArch(ElfHeader); +} + +template +void ELFStubBuilder::populateArch(const Elf_Ehdr *Header) { + Stub.Arch = Header->e_machine; +} + +std::unique_ptr ELFObjReader::readFile(MemoryBufferRef Buf) { + std::unique_ptr DestStub = make_unique(); + Expected> BinOrErr = createBinary(Buf); + if (!BinOrErr) { + consumeError(BinOrErr.takeError()); + return nullptr; + } + + Binary *Bin = BinOrErr->get(); + if (auto Obj = dyn_cast>(Bin)) { + ELFStubBuilder Builder(Obj, *DestStub); + Builder.build(); + } else if (auto Obj = dyn_cast>(Bin)) { + ELFStubBuilder Builder(Obj, *DestStub); + Builder.build(); + } else if (auto Obj = dyn_cast>(Bin)) { + ELFStubBuilder Builder(Obj, *DestStub); + Builder.build(); + } else if (auto Obj = dyn_cast>(Bin)) { + ELFStubBuilder Builder(Obj, *DestStub); + Builder.build(); + } else { + terminateWithError(createStringError(errc::not_supported, + "Unsupported ELF file architecture.")); + } + + return DestStub; +} Index: llvm/tools/llvm-elfabi/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-elfabi/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-elfabi/LLVMBuild.txt ------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-elfabi +parent = Tools +required_libraries = Object Support TextAPI Index: llvm/tools/llvm-elfabi/llvm-elfabi.h =================================================================== --- /dev/null +++ llvm/tools/llvm-elfabi/llvm-elfabi.h @@ -0,0 +1,28 @@ +//===- llvm-elfabi.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TOOLS_ELFABI_ELFABI_H +#define LLVM_TOOLS_ELFABI_ELFABI_H + +#include "llvm/Support/Compiler.h" +#include + +namespace llvm { + +class Error; + +namespace elfabi { + +LLVM_ATTRIBUTE_NORETURN void terminateWithError(std::error_code Err); +LLVM_ATTRIBUTE_NORETURN void terminateWithError(Error Err); + +} // end namespace elfabi +} // end namespace llvm + +#endif // LLVM_TOOLS_ELFABI_ELFABI_H Index: llvm/tools/llvm-elfabi/llvm-elfabi.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-elfabi/llvm-elfabi.cpp @@ -0,0 +1,136 @@ +//===- llvm-elfabi.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#include "llvm-elfabi.h" +#include "ELFObjHandler.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TextAPI/ELF/TBEHandler.h" +#include + +namespace llvm { +namespace elfabi { + +TBEHandler TextHandler; +ELFObjReader BinaryReader; + +cl::opt InputFilePath(cl::Positional, cl::desc("input"), + cl::Required); +cl::opt + EmitTBE("emit-tbe", + cl::desc("Emit a text-based ELF stub (.tbe) from the input file"), + cl::value_desc("")); +cl::opt SetSoname( + "so-name", + cl::desc("Manually set the DT_SONAME entry of any emitted files."), + cl::value_desc("")); + +LLVM_ATTRIBUTE_NORETURN void terminateWithError(std::error_code Err) { + errs() << "Error: " << Err.message() << "\n"; + exit(1); +} + +LLVM_ATTRIBUTE_NORETURN void terminateWithError(Error Err) { + errs() << "Error: " << Err << "\n"; + exit(1); +} + +} // end namespace elfabi +} // end namespace llvm + +using namespace llvm; +using namespace llvm::elfabi; + +/// updateSoName() sets DT_SONAME for a stub if the file doesn't have a +/// DT_SONAME (defaults to "[file name stem].so"), or sets to a user-provided +/// soname. +static void updateSoName(ELFStub &Stub, StringRef FileName) { + if (SetSoname.getNumOccurrences() == 1) { + Stub.SoName = SetSoname; + } else if (Stub.SoName.length() == 0) { + Stub.SoName = FileName; + Stub.SoName.append(".so"); + } +} + +/// writeTBE() writes a Text-Based ELF stub to a file using the latest version +/// of the YAML parser. +static void writeTBE(StringRef FilePath, ELFStub &Stub) { + std::error_code SysErr; + std::string OldSoName = Stub.SoName; + VersionTuple OldVersion = Stub.TbeVersion; + + // Open file for writing. + raw_fd_ostream Out(FilePath, SysErr); + if (SysErr) + terminateWithError(SysErr); + + // Update SoName for output files. + updateSoName(Stub, sys::path::stem(FilePath)); + Stub.TbeVersion = TBEVersionCurrent; + + // Write file. + Error ReadErr = TextHandler.writeFile(Out, Stub); + if (ReadErr) + terminateWithError(std::move(ReadErr)); + + // Revert SoName so logic is handled properly on any other emitted files. + Stub.SoName = OldSoName; + Stub.TbeVersion = OldVersion; +} + +/// readInputFunction to populate an ELFStub by attempting to read the +/// input file using both the TBE and binary ELF parsers. +static Expected> readInputFile(StringRef FilePath) { + // Read in file. + std::unique_ptr Stub; + ErrorOr> BufOrError = + MemoryBuffer::getFile(FilePath); + if (!BufOrError) { + return createStringError(BufOrError.getError(), "unable to read file"); + } + + std::unique_ptr FileReadBuffer = std::move(BufOrError.get()); + + // First try to read as a binary (fails fast if not binary). + Stub = BinaryReader.readFile(FileReadBuffer->getMemBufferRef()); + if (Stub.get() != nullptr) + return Stub; + + // Fall back to reading as a tbe. + Stub = TextHandler.readFile(FileReadBuffer->getBuffer()); + if (Stub.get() != nullptr) + return Stub; + + return createStringError(errc::not_supported, "unsupported file format"); +} + +int main(int argc, char *argv[]) { + // Parse arguments. + cl::ParseCommandLineOptions(argc, argv); + std::unique_ptr TargetStub; + + Expected> StubOrErr = readInputFile(InputFilePath); + if (!StubOrErr) { + errs() << "Failed to load input file:\n"; + terminateWithError(StubOrErr.takeError()); + } + + TargetStub = std::move(StubOrErr.get()); + + // Write out File. + if (EmitTBE.getNumOccurrences() == 1) { + writeTBE(EmitTBE, *TargetStub); + } + return 0; +}