From 2ce379f0b36310540322589a3aaae8773bf59938 Mon Sep 17 00:00:00 2001 From: Pavel Kopyl Date: Thu, 8 Feb 2024 14:05:06 +0100 Subject: [PATCH] [LLD] Adding EVM architecture to LLD. --- lld/ELF/Arch/EVM.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++ lld/ELF/CMakeLists.txt | 1 + lld/ELF/Driver.cpp | 3 ++ lld/ELF/InputFiles.cpp | 4 ++ lld/ELF/Target.cpp | 4 ++ lld/ELF/Target.h | 3 ++ 6 files changed, 109 insertions(+) create mode 100644 lld/ELF/Arch/EVM.cpp diff --git a/lld/ELF/Arch/EVM.cpp b/lld/ELF/Arch/EVM.cpp new file mode 100644 index 000000000000..920017817c9a --- /dev/null +++ b/lld/ELF/Arch/EVM.cpp @@ -0,0 +1,94 @@ +//===- EVM.cpp ------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// EVM is a stack-based virtual machine with a word size of 256 bits intendent +// for execution of smart contracts in Ethereum blockchain environment. +// +// Since it is a baremetal programming, there's usually no loader to load +// ELF files on EVMs. You are expected to link your program against address +// 0 and pull out a .text section from the result using objcopy, so that you +// can write the linked code to on-chip flush memory. You can do that with +// the following commands: +// +// ld.lld -Ttext=0 -o foo foo.o +// objcopy -O binary --only-section=.text foo output.bin +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +class EVM final : public TargetInfo { +public: + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const override; + void relocate(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; +}; +} // namespace + +RelExpr EVM::getRelExpr(RelType type, const Symbol &s, + const uint8_t *loc) const { + switch (type) { + case R_EVM_DATA: + return R_ABS; + default: + error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + + ") against symbol " + toString(s)); + return R_NONE; + } +} + +void EVM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const { + switch (rel.type) { + case R_EVM_DATA: { + if (val > std::numeric_limits::max()) + llvm_unreachable("R_EVM_DATA: to big relocation value"); + write32be(loc, val); + break; + } + default: + llvm_unreachable("unknown relocation"); + } +} + +TargetInfo *elf::getEVMTargetInfo() { + static EVM target; + return ⌖ +} + +static uint32_t getEFlags(InputFile *file) { + return cast>(file)->getObj().getHeader().e_flags; +} + +uint32_t EVM::calcEFlags() const { + assert(!ctx.objectFiles.empty()); + + const uint32_t flags = getEFlags(ctx.objectFiles[0]); + for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) { + const uint32_t objFlags = getEFlags(f); + if ((objFlags /* & EF_EVM_ARCH_MASK*/) != (flags /* & EF_EVM_ARCH_MASK*/)) + error(toString(f) + + ": cannot link object files with incompatible target ISA"); + } + + return flags; +} diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index df509c29b4d8..b4d3c36323c3 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -24,6 +24,7 @@ add_lld_library(lldELF Arch/AMDGPU.cpp Arch/ARM.cpp Arch/AVR.cpp + Arch/EVM.cpp Arch/EraVM.cpp Arch/Hexagon.cpp Arch/LoongArch.cpp diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 2ce13c10342f..a1850456ed45 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -52,6 +52,9 @@ #include "llvm/Config/llvm-config.h" #include "llvm/LTO/LTO.h" #include "llvm/Object/Archive.h" +// EVM local begin +#include "llvm/Object/ELF.h" +// EVM local end #include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index 2ecdd74cd6b3..b5c8ba088fa7 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1614,6 +1614,10 @@ static uint16_t getBitcodeMachineKind(StringRef path, const Triple &t) { return t.isOSIAMCU() ? EM_IAMCU : EM_386; case Triple::x86_64: return EM_X86_64; + // EVM local begin + case Triple::evm: + return EM_EVM; + // EVM local end default: error(path + ": could not infer e_machine from bitcode target triple " + t.str()); diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index d29d0d156950..08d8d5b0804d 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -91,6 +91,10 @@ TargetInfo *elf::getTarget() { return getSPARCV9TargetInfo(); case EM_X86_64: return getX86_64TargetInfo(); + // EVM local begin + case EM_EVM: + return getEVMTargetInfo(); + // EVM local end } llvm_unreachable("unknown target machine"); } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index a77fe0213ae0..3097bc2bae60 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -188,6 +188,9 @@ TargetInfo *getRISCVTargetInfo(); TargetInfo *getSPARCV9TargetInfo(); TargetInfo *getX86TargetInfo(); TargetInfo *getX86_64TargetInfo(); +// EVM local begin +TargetInfo *getEVMTargetInfo(); +// EVM local end template TargetInfo *getMipsTargetInfo(); struct ErrorPlace {