diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF.h @@ -0,0 +1,31 @@ +//===------- ELF.h - Generic JIT link function for ELF ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Generic jit-link functions for ELF. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_H +#define LLVM_EXECUTIONENGINE_JITLINK_ELF_H + +#include "llvm/ExecutionEngine/JITLink/ELF.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +/// jit-link the given ObjBuffer, which must be a ELF object file. +/// +/// Uses conservative defaults for GOT and stub handling based on the target +/// platform. +void jitLink_ELF(std::unique_ptr Ctx); + +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_H diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_x86_64.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_x86_64.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_x86_64.h @@ -0,0 +1,55 @@ +//===--- ELF_x86_64.h - JIT link functions for ELF/x86-64 ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// jit-link functions for ELF/x86-64. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_X86_64_H +#define LLVM_EXECUTIONENGINE_JITLINK_ELF_X86_64_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm { +namespace jitlink { + +namespace ELF_x86_64_Edges { + +enum ELFX86RelocationKind : Edge::Kind { + R_AMD64_NONE = Edge::FirstRelocation, + R_AMD64_64, + R_AMD64_PC32, + R_AMD64_GOT32, + R_AMD64_PLT32, + R_AMD64_COPY, + R_AMD64_GLOB_DAT, + R_AMD64_JUMP_SLOT, + R_AMD64_RELATIVE, + R_AMD64_GOTPCREL, + R_AMD64_32, + R_AMD64_32S, + R_AMD64_16, + R_AMD64_PC16, + R_AMD64_8, + R_AMD64_PC8, + R_AMD64_PC64, + R_AMD64_GOTOFF64, + R_AMD64_GOTPC32, + R_AMD64_SIZE32, + R_AMD64_SIZE64 +}; + +} // end namespace ELF_x86_64_Edges + +/// jit-link the given object buffer, which must be a ELF x86-64 object file. +void jitLink_ELF_x86_64(std::unique_ptr Ctx); +StringRef getELFX86RelocationKindName(Edge::Kind R); +} // end namespace jitlink +} // end namespace llvm + +#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_X86_64_H diff --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -3,10 +3,14 @@ JITLinkGeneric.cpp JITLinkMemoryManager.cpp EHFrameSupport.cpp + #macho MachO.cpp MachO_arm64.cpp MachO_x86_64.cpp MachOLinkGraphBuilder.cpp + #elf + ELF.cpp + ELF_x86_64.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/JITLink @@ -14,7 +18,7 @@ DEPENDS intrinsics_gen ) - + target_link_libraries(LLVMJITLink PRIVATE LLVMObject diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp @@ -0,0 +1,51 @@ +//===-------------- ELF.cpp - JIT linker function for ELF -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// ELF jit-link function. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/ELF.h" + +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MemoryBuffer.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "jitlink" + +namespace llvm { +namespace jitlink { + +void jitLink_ELF(std::unique_ptr Ctx) { + + // We don't want to do full ELF validation here. We just verify it is elf'ish. + // Probably should parse into an elf header when we support more than x86 :) + + StringRef Data = Ctx->getObjectBuffer().getBuffer(); + if (Data.size() < llvm::ELF::EI_MAG3 + 1) { + Ctx->notifyFailed(make_error("Truncated ELF buffer")); + return; + } + + if (!memcmp(Data.data(), llvm::ELF::ElfMagic, strlen(llvm::ELF::ElfMagic))) { + if (Data.data()[llvm::ELF::EI_CLASS] == ELF::ELFCLASS64) { + return jitLink_ELF_x86_64(std::move(Ctx)); + } + } + + Ctx->notifyFailed(make_error("ELF magic not valid")); +} + +} // end namespace jitlink +} // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_x86_64.cpp @@ -0,0 +1,382 @@ +//===---- ELF_x86_64.cpp -JIT linker implementation for ELF/x86-64 ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// ELF/x86-64 jit-link implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h" +#include "JITLinkGeneric.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/Object/ELFObjectFile.h" + +#define DEBUG_TYPE "jitlink" + +using namespace llvm; +using namespace llvm::jitlink; + +static const char *CommonSectionName = "__common"; + +namespace llvm { +namespace jitlink { +// This should become a template as the ELFFile is so a lot of this could become +// generic +class ELFLinkGraphBuilder_x86_64 { + +private: + Section *CommonSection = nullptr; + Section &getCommonSection() { + if (!CommonSection) { + auto Prot = static_cast( + sys::Memory::MF_READ | sys::Memory::MF_WRITE); + CommonSection = &G->createSection(CommonSectionName, Prot); + } + return *CommonSection; + } + + std::unique_ptr G; + // This could be a template + const object::ELFFile &Obj; + object::ELFFile::Elf_Shdr_Range sections; + + bool isRelocatable() { return Obj.getHeader()->e_type == llvm::ELF::ET_REL; } + + support::endianness + getEndianness(const object::ELFFile &Obj) { + return Obj.isLE() ? support::little : support::big; + } + + // This could also just become part of a template + unsigned getPointerSize(const object::ELFFile &Obj) { + return Obj.getHeader()->getFileClass() == ELF::ELFCLASS64 ? 8 : 4; + } + + // We don't technically need this right now + // But for now going to keep it as it helps me to debug things + + Error createNormalizedSymbols() { + LLVM_DEBUG(dbgs() << "Creating normalized symbols...\n"); + + for (auto SecRef : sections) { + if (SecRef.sh_type != ELF::SHT_SYMTAB && + SecRef.sh_type != ELF::SHT_DYNSYM) + continue; + + auto Symbols = Obj.symbols(&SecRef); + // TODO: Currently I use this function to test things + // I also want to leave it to see if its common between MACH and elf + // so for now I just want to continue even if there is an error + if (errorToBool(Symbols.takeError())) + continue; + + auto StrTabSec = Obj.getSection(SecRef.sh_link); + if (!StrTabSec) + return StrTabSec.takeError(); + auto StringTable = Obj.getStringTable(*StrTabSec); + if (!StringTable) + return StringTable.takeError(); + + for (auto SymRef : *Symbols) { + Optional Name; + unsigned char Binding; + uint64_t Value; + uint64_t Size = 0; + + // FIXME: Read size. + (void)Size; + + if (auto NameOrErr = SymRef.getName(*StringTable)) { + Name = *NameOrErr; + } else { + return NameOrErr.takeError(); + } + Binding = SymRef.getBinding(); + Value = SymRef.getValue(); + LLVM_DEBUG({ + dbgs() << " "; + if (!Name) + dbgs() << ""; + else + dbgs() << *Name; + dbgs() << ": value = " << formatv("{0:x16}", Value) + << ", type = " << formatv("{0:x2}", SymRef.getType()) + << ", binding = " << Binding + << ", size =" << Size; + dbgs() << "\n"; + }); + } + } + return Error::success(); + } + + Error createNormalizedSections() { + LLVM_DEBUG(dbgs() << "Creating normalized sections...\n"); + for (auto &SecRef : sections) { + auto Name = Obj.getSectionName(&SecRef); + if (!Name) + return Name.takeError(); + sys::Memory::ProtectionFlags Prot; + if (SecRef.sh_flags & ELF::SHF_EXECINSTR) { + Prot = static_cast(sys::Memory::MF_READ | + sys::Memory::MF_EXEC); + } else { + Prot = static_cast(sys::Memory::MF_READ | + sys::Memory::MF_WRITE); + } + uint64_t Address = SecRef.sh_addr; + uint64_t Size = SecRef.sh_size; + uint64_t Flags = SecRef.sh_flags; + uint64_t Alignment = SecRef.sh_addralign; + const char *Data = nullptr; + // TODO: figure out what it is that has 0 size no name and address + // 0000-0000 + if (Size == 0) + continue; + + // FIXME: Use flags. + (void)Flags; + + LLVM_DEBUG({ + dbgs() << " " << *Name << ": " << formatv("{0:x16}", Address) << " -- " + << formatv("{0:x16}", Address + Size) << ", align: " << Alignment + << " Flags:" << Flags << "\n"; + }); + + if (SecRef.sh_type != ELF::SHT_NOBITS) { + // .sections() already checks that the data is not beyond the end of + // file + auto contents = Obj.getSectionContentsAsArray(&SecRef); + if (!contents) + return contents.takeError(); + + Data = contents->data(); + // TODO protection flags. + // for now everything is + auto §ion = G->createSection(*Name, Prot); + // Do this here because we have it, but move it into graphify later + G->createContentBlock(section, StringRef(Data, Size), Address, + Alignment, 0); + } + } + + return Error::success(); + } + + Error graphifyRegularSymbols() { + + // TODO: ELF supports beyond SHN_LORESERVE, + // need to perf test how a vector vs map handles those cases + + std::vector::Elf_Shdr_Range *>> + SecIndexToSymbols; + + LLVM_DEBUG(dbgs() << "Creating graph symbols...\n"); + + for (auto SecRef : sections) { + + if (SecRef.sh_type != ELF::SHT_SYMTAB && + SecRef.sh_type != ELF::SHT_DYNSYM) + continue; + auto Symbols = Obj.symbols(&SecRef); + if (!Symbols) + return Symbols.takeError(); + + auto StrTabSec = Obj.getSection(SecRef.sh_link); + if (!StrTabSec) + return StrTabSec.takeError(); + auto StringTable = Obj.getStringTable(*StrTabSec); + if (!StringTable) + return StringTable.takeError(); + auto Name = Obj.getSectionName(&SecRef); + if (!Name) + return Name.takeError(); + auto Section = G->findSectionByName(*Name); + if (!Section) + return make_error("Could not find a section", + llvm::inconvertibleErrorCode()); + // we only have one for now + auto blocks = Section->blocks(); + if (blocks.empty()) + return make_error("Section has no block", + llvm::inconvertibleErrorCode()); + + for (auto SymRef : *Symbols) { + auto Type = SymRef.getType(); + if (Type == ELF::STT_NOTYPE || Type == ELF::STT_FILE) + continue; + // these should do it for now + // if(Type != ELF::STT_NOTYPE && + // Type != ELF::STT_OBJECT && + // Type != ELF::STT_FUNC && + // Type != ELF::STT_SECTION && + // Type != ELF::STT_COMMON) { + // continue; + // } + std::pair bindings; + auto Name = SymRef.getName(*StringTable); + // I am not sure on If this is going to hold as an invariant. Revisit. + if (!Name) + return Name.takeError(); + // TODO: weak and hidden + if (SymRef.isExternal()) { + bindings = {Linkage::Strong, Scope::Default}; + } else { + bindings = {Linkage::Strong, Scope::Local}; + } + + if (SymRef.isDefined() && + (Type == ELF::STT_FUNC || Type == ELF::STT_OBJECT)) { + + auto DefinedSection = Obj.getSection(SymRef.st_shndx); + if (!DefinedSection) + return DefinedSection.takeError(); + auto sectName = Obj.getSectionName(*DefinedSection); + if (!sectName) + return Name.takeError(); + + auto JitSection = G->findSectionByName(*sectName); + if (!JitSection) + return make_error( + "Could not find a section", llvm::inconvertibleErrorCode()); + auto bs = JitSection->blocks(); + if (bs.empty()) + return make_error( + "Section has no block", llvm::inconvertibleErrorCode()); + + auto B = *bs.begin(); + LLVM_DEBUG({ dbgs() << " " << *Name << ": "; }); + + G->addDefinedSymbol(*B, SymRef.getValue(), *Name, SymRef.st_size, + bindings.first, bindings.second, + SymRef.getType() == ELF::STT_FUNC, false); + } + //TODO: The following has to be implmented. + // leaving commented out to save time for future patchs + /* + G->addAbsoluteSymbol(*Name, SymRef.getValue(), SymRef.st_size, + Linkage::Strong, Scope::Default, false); + + if(SymRef.isCommon()) { + G->addCommonSymbol(*Name, Scope::Default, getCommonSection(), 0, 0, + SymRef.getValue(), false); + } + + + //G->addExternalSymbol(*Name, SymRef.st_size, Linkage::Strong); + */ + } + } + return Error::success(); + } + +public: + ELFLinkGraphBuilder_x86_64(std::string filename, + const object::ELFFile &Obj) + : G(std::make_unique(filename, getPointerSize(Obj), + getEndianness(Obj))), + Obj(Obj) {} + + Expected> buildGraph() { + // Sanity check: we only operate on relocatable objects. + if (!isRelocatable()) + return make_error("Object is not a relocatable ELF"); + + auto Secs = Obj.sections(); + + if (!Secs) { + return Secs.takeError(); + } + sections = *Secs; + + if (auto Err = createNormalizedSections()) + return std::move(Err); + + if (auto Err = createNormalizedSymbols()) + return std::move(Err); + + if (auto Err = graphifyRegularSymbols()) + return std::move(Err); + + return std::move(G); + } +}; + +class ELFJITLinker_x86_64 : public JITLinker { + friend class JITLinker; + +public: + ELFJITLinker_x86_64(std::unique_ptr Ctx, + PassConfiguration PassConfig) + : JITLinker(std::move(Ctx), std::move(PassConfig)) {} + +private: + StringRef getEdgeKindName(Edge::Kind R) const override { + return getELFX86RelocationKindName(R); + } + + Expected> + buildGraph(MemoryBufferRef ObjBuffer) override { + auto ELFObj = object::ObjectFile::createELFObjectFile(ObjBuffer); + if (!ELFObj) + return ELFObj.takeError(); + + auto &ELFObjFile = cast>(**ELFObj); + std::string fileName(ELFObj->get()->getFileName()); + return ELFLinkGraphBuilder_x86_64(std::move(fileName), + *ELFObjFile.getELFFile()) + .buildGraph(); + } + + Error applyFixup(Block &B, const Edge &E, char *BlockWorkingMem) const { + //TODO: add relocation handling + return Error::success(); + } +}; + +void jitLink_ELF_x86_64(std::unique_ptr Ctx) { + PassConfiguration Config; + Triple TT("x86_64-linux"); + // Construct a JITLinker and run the link function. + // Add a mark-live pass. + if (auto MarkLive = Ctx->getMarkLivePass(TT)) + Config.PrePrunePasses.push_back(std::move(MarkLive)); + else + Config.PrePrunePasses.push_back(markAllSymbolsLive); + + ELFJITLinker_x86_64::link(std::move(Ctx), std::move(Config)); +} + +StringRef getELFX86RelocationKindName(Edge::Kind R) { + switch (R) { + // case R_AMD64_NONE: + // return "None"; + // case R_AMD64_PC32: + // case R_AMD64_GOT32: + // caseR_AMD64_PLT32, + // R_AMD64_COPY, + // R_AMD64_GLOB_DAT, + // R_AMD64_JUMP_SLOT, + // R_AMD64_RELATIVE, + // R_AMD64_GOTPCREL, + // R_AMD64_32, + // R_AMD64_32S, + // R_AMD64_16, + // R_AMD64_PC16, + // R_AMD64_8, + // R_AMD64_PC8, + // R_AMD64_PC64, + // R_AMD64_GOTOFF64, + // R_AMD64_GOTPC32, + // R_AMD64_SIZE32, + // R_AMD64_SIZE64 + default: + return getGenericEdgeKindName(static_cast(R)); + } +} +} // end namespace jitlink +} // end namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp --- a/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/JITLink.cpp @@ -10,6 +10,7 @@ #include "llvm/ExecutionEngine/JITLink/JITLink.h" #include "llvm/BinaryFormat/Magic.h" +#include "llvm/ExecutionEngine/JITLink/ELF.h" #include "llvm/ExecutionEngine/JITLink/MachO.h" #include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" @@ -300,6 +301,8 @@ switch (Magic) { case file_magic::macho_object: return jitLink_MachO(std::move(Ctx)); + case file_magic::elf_relocatable: + return jitLink_ELF(std::move(Ctx)); default: Ctx->notifyFailed(make_error("Unsupported file format")); }; diff --git a/llvm/test/ExecutionEngine/JITLink/X86/ELF_x86-64_relocations.s b/llvm/test/ExecutionEngine/JITLink/X86/ELF_x86-64_relocations.s new file mode 100644 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/X86/ELF_x86-64_relocations.s @@ -0,0 +1,20 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc -triple=x86_64-unknown-linux -filetype=obj -o %t/elf_reloc.o %s +# RUN: llvm-jitlink -noexec %t/elf_reloc.o +# +# Test standard ELF relocations. + + .text + .file "testcase.c" + .globl main + .p2align 4, 0x90 + .type main,@function +main: + movl $42, %eax + retq +.Lfunc_end0: + .size main, .Lfunc_end0-main + + .ident "clang version 10.0.0-4ubuntu1 " + .section ".note.GNU-stack","",@progbits + .addrsig \ No newline at end of file