1//===---- llvm-jitlink-elf.cpp -- ELF parsing support for llvm-jitlink ----===//
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 parsing support for llvm-jitlink.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm-jitlink.h"
14
15#include "llvm/Support/Error.h"
16#include "llvm/Support/Path.h"
17
18#define DEBUG_TYPE "llvm_jitlink"
19
20using namespace llvm;
21using namespace llvm::jitlink;
22
23static bool isELFGOTSection(Section &S) { return S.getName() == "$__GOT"; }
24
25static bool isELFStubsSection(Section &S) { return S.getName() == "$__STUBS"; }
26
27static bool isELFAArch32StubsSection(Section &S) {
28 return S.getName().starts_with(Prefix: "__llvm_jitlink_aarch32_STUBS_");
29}
30
31static Expected<Edge &> getFirstRelocationEdge(LinkGraph &G, Block &B) {
32 auto EItr =
33 llvm::find_if(Range: B.edges(), P: [](Edge &E) { return E.isRelocation(); });
34 if (EItr == B.edges().end())
35 return make_error<StringError>(Args: "GOT entry in " + G.getName() + ", \"" +
36 B.getSection().getName() +
37 "\" has no relocations",
38 Args: inconvertibleErrorCode());
39 return *EItr;
40}
41
42static Expected<Symbol &> getELFGOTTarget(LinkGraph &G, Block &B) {
43 auto E = getFirstRelocationEdge(G, B);
44 if (!E)
45 return E.takeError();
46 auto &TargetSym = E->getTarget();
47 if (!TargetSym.hasName())
48 return make_error<StringError>(
49 Args: "GOT entry in " + G.getName() + ", \"" +
50 TargetSym.getBlock().getSection().getName() +
51 "\" points to anonymous "
52 "symbol",
53 Args: inconvertibleErrorCode());
54 return TargetSym;
55}
56
57static Expected<Symbol &> getELFStubTarget(LinkGraph &G, Block &B) {
58 auto E = getFirstRelocationEdge(G, B);
59 if (!E)
60 return E.takeError();
61 auto &GOTSym = E->getTarget();
62 if (!GOTSym.isDefined())
63 return make_error<StringError>(Args: "Stubs entry in " + G.getName() +
64 " does not point to GOT entry",
65 Args: inconvertibleErrorCode());
66 if (!isELFGOTSection(S&: GOTSym.getBlock().getSection()))
67 return make_error<StringError>(
68 Args: "Stubs entry in " + G.getName() + ", \"" +
69 GOTSym.getBlock().getSection().getName() +
70 "\" does not point to GOT entry",
71 Args: inconvertibleErrorCode());
72 return getELFGOTTarget(G, B&: GOTSym.getBlock());
73}
74
75static Expected<Symbol &> getELFAArch32StubTarget(LinkGraph &G, Block &B) {
76 auto E = getFirstRelocationEdge(G, B);
77 if (!E)
78 return E.takeError();
79 return E->getTarget();
80}
81
82enum SectionType { GOT, Stubs, AArch32Stubs, Other };
83
84static Error registerSymbol(LinkGraph &G, Symbol &Sym, Session::FileInfo &FI,
85 SectionType SecType) {
86 switch (SecType) {
87 case GOT:
88 if (Sym.getSize() == 0)
89 return Error::success(); // Skip the GOT start symbol
90 return FI.registerGOTEntry(G, Sym, GetSymbolTarget: getELFGOTTarget);
91 case Stubs:
92 return FI.registerStubEntry(G, Sym, GetSymbolTarget: getELFStubTarget);
93 case AArch32Stubs:
94 return FI.registerMultiStubEntry(G, Sym, GetSymbolTarget: getELFAArch32StubTarget);
95 case Other:
96 return Error::success();
97 }
98 llvm_unreachable("Unhandled SectionType enum");
99}
100
101namespace llvm {
102
103Error registerELFGraphInfo(Session &S, LinkGraph &G) {
104 std::lock_guard<std::mutex> Lock(S.M);
105
106 auto FileName = sys::path::filename(path: G.getName());
107 auto [It, Inserted] = S.FileInfos.try_emplace(Key: FileName);
108 if (!Inserted) {
109 return make_error<StringError>(Args: "When -check is passed, file names must be "
110 "distinct (duplicate: \"" +
111 FileName + "\")",
112 Args: inconvertibleErrorCode());
113 }
114
115 auto &FileInfo = It->second;
116 LLVM_DEBUG({
117 dbgs() << "Registering ELF file info for \"" << FileName << "\"\n";
118 });
119 for (auto &Sec : G.sections()) {
120 LLVM_DEBUG({
121 dbgs() << " Section \"" << Sec.getName() << "\": "
122 << (Sec.symbols().empty() ? "empty. skipping." : "processing...")
123 << "\n";
124 });
125
126 // Skip empty sections.
127 if (Sec.symbols().empty())
128 continue;
129
130 if (FileInfo.SectionInfos.count(Key: Sec.getName()))
131 return make_error<StringError>(Args: "Encountered duplicate section name \"" +
132 Sec.getName() + "\" in \"" + FileName +
133 "\"",
134 Args: inconvertibleErrorCode());
135
136 SectionType SecType;
137 if (isELFGOTSection(S&: Sec)) {
138 SecType = GOT;
139 } else if (isELFStubsSection(S&: Sec)) {
140 SecType = Stubs;
141 } else if (isELFAArch32StubsSection(S&: Sec)) {
142 SecType = AArch32Stubs;
143 } else {
144 SecType = Other;
145 }
146
147 bool SectionContainsContent = false;
148 bool SectionContainsZeroFill = false;
149
150 auto *FirstSym = *Sec.symbols().begin();
151 auto *LastSym = FirstSym;
152 for (auto *Sym : Sec.symbols()) {
153 if (Sym->getAddress() < FirstSym->getAddress())
154 FirstSym = Sym;
155 if (Sym->getAddress() > LastSym->getAddress())
156 LastSym = Sym;
157
158 if (SecType != Other) {
159 if (Error Err = registerSymbol(G, Sym&: *Sym, FI&: FileInfo, SecType))
160 return Err;
161 SectionContainsContent = true;
162 }
163
164 if (Sym->hasName()) {
165 if (Sym->isSymbolZeroFill()) {
166 S.SymbolInfos[Sym->getName()] = {Sym->getSize(),
167 Sym->getAddress().getValue()};
168 SectionContainsZeroFill = true;
169 } else {
170 S.SymbolInfos[Sym->getName()] = {Sym->getSymbolContent(),
171 Sym->getAddress().getValue(),
172 Sym->getTargetFlags()};
173 SectionContainsContent = true;
174 }
175 }
176 }
177
178 // Add symbol info for absolute symbols.
179 for (auto *Sym : G.absolute_symbols())
180 S.SymbolInfos[Sym->getName()] = {Sym->getSize(),
181 Sym->getAddress().getValue()};
182
183 auto SecAddr = FirstSym->getAddress();
184 auto SecSize =
185 (LastSym->getBlock().getAddress() + LastSym->getBlock().getSize()) -
186 SecAddr;
187
188 if (SectionContainsZeroFill && SectionContainsContent)
189 return make_error<StringError>(Args: "Mixed zero-fill and content sections not "
190 "supported yet",
191 Args: inconvertibleErrorCode());
192 if (SectionContainsZeroFill)
193 FileInfo.SectionInfos[Sec.getName()] = {SecSize, SecAddr.getValue()};
194 else
195 FileInfo.SectionInfos[Sec.getName()] = {
196 ArrayRef<char>(FirstSym->getBlock().getContent().data(), SecSize),
197 SecAddr.getValue(), FirstSym->getTargetFlags()};
198 }
199
200 return Error::success();
201}
202
203} // end namespace llvm
204