1 //===----- ELF_aarch64.cpp - JIT linker implementation for ELF/aarch64 ----===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // ELF/aarch64 jit-link implementation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/ExecutionEngine/JITLink/ELF_aarch64.h"
14 #include "ELFLinkGraphBuilder.h"
15 #include "JITLinkGeneric.h"
16 #include "llvm/BinaryFormat/ELF.h"
17 #include "llvm/ExecutionEngine/JITLink/aarch64.h"
18 #include "llvm/Object/ELFObjectFile.h"
19 
20 #define DEBUG_TYPE "jitlink"
21 
22 using namespace llvm;
23 using namespace llvm::jitlink;
24 
25 namespace llvm {
26 namespace jitlink {
27 
28 class ELFJITLinker_aarch64 : public JITLinker<ELFJITLinker_aarch64> {
29   friend class JITLinker<ELFJITLinker_aarch64>;
30 
31 public:
32   ELFJITLinker_aarch64(std::unique_ptr<JITLinkContext> Ctx,
33                        std::unique_ptr<LinkGraph> G,
34                        PassConfiguration PassConfig)
35       : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
36 
37 private:
38   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
39     using namespace aarch64;
40     using namespace llvm::support;
41 
42     char *BlockWorkingMem = B.getAlreadyMutableContent().data();
43     char *FixupPtr = BlockWorkingMem + E.getOffset();
44     JITTargetAddress FixupAddress = B.getAddress() + E.getOffset();
45     switch (E.getKind()) {
46     case aarch64::R_AARCH64_CALL26: {
47       assert((FixupAddress & 0x3) == 0 && "Call-inst is not 32-bit aligned");
48       int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
49 
50       if (static_cast<uint64_t>(Value) & 0x3)
51         return make_error<JITLinkError>("Call target is not 32-bit aligned");
52 
53       if (!fitsRangeSignedInt<27>(Value))
54         return makeTargetOutOfRangeError(G, B, E);
55 
56       uint32_t RawInstr = *(little32_t *)FixupPtr;
57       assert((RawInstr & 0x7fffffff) == 0x14000000 &&
58              "RawInstr isn't a B or BR immediate instruction");
59       uint32_t Imm = (static_cast<uint32_t>(Value) & ((1 << 28) - 1)) >> 2;
60       uint32_t FixedInstr = RawInstr | Imm;
61       *(little32_t *)FixupPtr = FixedInstr;
62       break;
63     }
64     }
65     return Error::success();
66   }
67 
68   template <uint8_t Bits> static bool fitsRangeSignedInt(int64_t Value) {
69     return Value >= -(1 << Bits) && Value < (1 << Bits);
70   }
71 };
72 
73 template <typename ELFT>
74 class ELFLinkGraphBuilder_aarch64 : public ELFLinkGraphBuilder<ELFT> {
75 private:
76   static Expected<aarch64::EdgeKind_aarch64>
77   getRelocationKind(const uint32_t Type) {
78     using namespace aarch64;
79     switch (Type) {
80     case ELF::R_AARCH64_CALL26:
81       return EdgeKind_aarch64::R_AARCH64_CALL26;
82     }
83 
84     return make_error<JITLinkError>("Unsupported aarch64 relocation:" +
85                                     formatv("{0:d}", Type));
86   }
87 
88   Error addRelocations() override {
89     using Base = ELFLinkGraphBuilder<ELFT>;
90     LLVM_DEBUG(dbgs() << "Adding relocations\n");
91 
92     // Iterate sections and only process the interesting ones.
93     for (auto &SecRef : Base::Sections) {
94       if (SecRef.sh_type != ELF::SHT_RELA && SecRef.sh_type != ELF::SHT_REL)
95         continue;
96       auto RelSectName = Base::Obj.getSectionName(SecRef);
97       if (!RelSectName)
98         return RelSectName.takeError();
99 
100       LLVM_DEBUG({
101         dbgs() << "Adding relocations from section " << *RelSectName << "\n";
102       });
103 
104       auto UpdateSect = Base::Obj.getSection(SecRef.sh_info);
105       if (!UpdateSect)
106         return UpdateSect.takeError();
107 
108       auto UpdateSectName = Base::Obj.getSectionName(**UpdateSect);
109       if (!UpdateSectName)
110         return UpdateSectName.takeError();
111 
112       // Don't process relocations for debug sections.
113       if (Base::isDwarfSection(*UpdateSectName)) {
114         LLVM_DEBUG({
115           dbgs() << "  Target is dwarf section " << *UpdateSectName
116                  << ". Skipping.\n";
117         });
118         continue;
119       }
120       LLVM_DEBUG(dbgs() << "  For target section " << *UpdateSectName << "\n");
121 
122       auto *JITSection = Base::G->findSectionByName(*UpdateSectName);
123       if (!JITSection)
124         return make_error<llvm::StringError>(
125             "Refencing a section that wasn't added to graph" + *UpdateSectName,
126             llvm::inconvertibleErrorCode());
127 
128       auto Relocations = Base::Obj.relas(SecRef);
129       if (!Relocations)
130         return Relocations.takeError();
131 
132       for (const auto &Rela : *Relocations) {
133         auto Type = Rela.getType(false);
134 
135         LLVM_DEBUG({
136           dbgs() << "Relocation Type: " << Type << "\n"
137                  << "Name: " << Base::Obj.getRelocationTypeName(Type) << "\n";
138         });
139 
140         auto SymbolIndex = Rela.getSymbol(false);
141         auto Symbol = Base::Obj.getRelocationSymbol(Rela, Base::SymTabSec);
142         if (!Symbol)
143           return Symbol.takeError();
144 
145         auto BlockToFix = *(JITSection->blocks().begin());
146         auto *TargetSymbol = Base::getGraphSymbol(SymbolIndex);
147 
148         if (!TargetSymbol) {
149           return make_error<llvm::StringError>(
150               "Could not find symbol at given index, did you add it to "
151               "JITSymbolTable? index: " +
152                   std::to_string(SymbolIndex) + ", shndx: " +
153                   std::to_string((*Symbol)->st_shndx) + " Size of table: " +
154                   std::to_string(Base::GraphSymbols.size()),
155               llvm::inconvertibleErrorCode());
156         }
157         int64_t Addend = Rela.r_addend;
158         JITTargetAddress FixupAddress = (*UpdateSect)->sh_addr + Rela.r_offset;
159 
160         LLVM_DEBUG({
161           dbgs() << "Processing relocation at "
162                  << format("0x%016" PRIx64, FixupAddress) << "\n";
163         });
164         auto Kind = getRelocationKind(Type);
165         if (!Kind)
166           return Kind.takeError();
167 
168         BlockToFix->addEdge(*Kind, FixupAddress - BlockToFix->getAddress(),
169                             *TargetSymbol, Addend);
170       }
171     }
172     return Error::success();
173   }
174 
175 public:
176   ELFLinkGraphBuilder_aarch64(StringRef FileName,
177                               const object::ELFFile<ELFT> &Obj, const Triple T)
178       : ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName,
179                                   aarch64::getEdgeKindName) {}
180 };
181 
182 Expected<std::unique_ptr<LinkGraph>>
183 createLinkGraphFromELFObject_aarch64(MemoryBufferRef ObjectBuffer) {
184   LLVM_DEBUG({
185     dbgs() << "Building jitlink graph for new input "
186            << ObjectBuffer.getBufferIdentifier() << "...\n";
187   });
188 
189   auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
190   if (!ELFObj)
191     return ELFObj.takeError();
192 
193   assert((*ELFObj)->getArch() == Triple::aarch64 &&
194          "Only AArch64 (little endian) is supported for now");
195 
196   auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
197   return ELFLinkGraphBuilder_aarch64<object::ELF64LE>((*ELFObj)->getFileName(),
198                                                       ELFObjFile.getELFFile(),
199                                                       (*ELFObj)->makeTriple())
200       .buildGraph();
201 }
202 
203 void link_ELF_aarch64(std::unique_ptr<LinkGraph> G,
204                       std::unique_ptr<JITLinkContext> Ctx) {
205   PassConfiguration Config;
206   const Triple &TT = G->getTargetTriple();
207   if (Ctx->shouldAddDefaultTargetPasses(TT)) {
208     if (auto MarkLive = Ctx->getMarkLivePass(TT))
209       Config.PrePrunePasses.push_back(std::move(MarkLive));
210     else
211       Config.PrePrunePasses.push_back(markAllSymbolsLive);
212   }
213   if (auto Err = Ctx->modifyPassConfig(*G, Config))
214     return Ctx->notifyFailed(std::move(Err));
215 
216   ELFJITLinker_aarch64::link(std::move(Ctx), std::move(G), std::move(Config));
217 }
218 
219 } // namespace jitlink
220 } // namespace llvm
221