1 | //===------ ObjectFileInterface.cpp - MU interface utils for objects ------===// |
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 | #include "llvm/ExecutionEngine/Orc/ObjectFileInterface.h" |
10 | #include "llvm/ExecutionEngine/JITSymbol.h" |
11 | #include "llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h" |
12 | #include "llvm/Object/COFF.h" |
13 | #include "llvm/Object/ELFObjectFile.h" |
14 | #include "llvm/Object/MachO.h" |
15 | #include "llvm/Object/ObjectFile.h" |
16 | #include "llvm/Object/XCOFFObjectFile.h" |
17 | #include <optional> |
18 | |
19 | #define DEBUG_TYPE "orc" |
20 | |
21 | namespace llvm { |
22 | namespace orc { |
23 | |
24 | void addInitSymbol(MaterializationUnit::Interface &I, ExecutionSession &ES, |
25 | StringRef ObjFileName) { |
26 | assert(!I.InitSymbol && "I already has an init symbol" ); |
27 | size_t Counter = 0; |
28 | |
29 | do { |
30 | std::string InitSymString; |
31 | raw_string_ostream(InitSymString) |
32 | << "$." << ObjFileName << ".__inits." << Counter++; |
33 | I.InitSymbol = ES.intern(SymName: InitSymString); |
34 | } while (I.SymbolFlags.count(Val: I.InitSymbol)); |
35 | |
36 | I.SymbolFlags[I.InitSymbol] = JITSymbolFlags::MaterializationSideEffectsOnly; |
37 | } |
38 | |
39 | static Expected<MaterializationUnit::Interface> |
40 | getMachOObjectFileSymbolInfo(ExecutionSession &ES, |
41 | const object::MachOObjectFile &Obj) { |
42 | MaterializationUnit::Interface I; |
43 | |
44 | for (auto &Sym : Obj.symbols()) { |
45 | Expected<uint32_t> SymFlagsOrErr = Sym.getFlags(); |
46 | if (!SymFlagsOrErr) |
47 | // TODO: Test this error. |
48 | return SymFlagsOrErr.takeError(); |
49 | |
50 | // Skip symbols not defined in this object file. |
51 | if (*SymFlagsOrErr & object::BasicSymbolRef::SF_Undefined) |
52 | continue; |
53 | |
54 | // Skip symbols that are not global. |
55 | if (!(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global)) |
56 | continue; |
57 | |
58 | // Skip symbols that have type SF_File. |
59 | if (auto SymType = Sym.getType()) { |
60 | if (*SymType == object::SymbolRef::ST_File) |
61 | continue; |
62 | } else |
63 | return SymType.takeError(); |
64 | |
65 | auto Name = Sym.getName(); |
66 | if (!Name) |
67 | return Name.takeError(); |
68 | auto SymFlags = JITSymbolFlags::fromObjectSymbol(Symbol: Sym); |
69 | if (!SymFlags) |
70 | return SymFlags.takeError(); |
71 | |
72 | // Strip the 'exported' flag from MachO linker-private symbols. |
73 | if (Name->starts_with(Prefix: "l" )) |
74 | *SymFlags &= ~JITSymbolFlags::Exported; |
75 | |
76 | I.SymbolFlags[ES.intern(SymName: *Name)] = std::move(*SymFlags); |
77 | } |
78 | |
79 | for (auto &Sec : Obj.sections()) { |
80 | auto SecType = Obj.getSectionType(Sec); |
81 | if ((SecType & MachO::SECTION_TYPE) == MachO::S_MOD_INIT_FUNC_POINTERS) { |
82 | addInitSymbol(I, ES, ObjFileName: Obj.getFileName()); |
83 | break; |
84 | } |
85 | auto SegName = Obj.getSectionFinalSegmentName(Sec: Sec.getRawDataRefImpl()); |
86 | auto SecName = cantFail(ValOrErr: Obj.getSectionName(Sec: Sec.getRawDataRefImpl())); |
87 | if (isMachOInitializerSection(SegName, SecName)) { |
88 | addInitSymbol(I, ES, ObjFileName: Obj.getFileName()); |
89 | break; |
90 | } |
91 | } |
92 | |
93 | return I; |
94 | } |
95 | |
96 | static Expected<MaterializationUnit::Interface> |
97 | getELFObjectFileSymbolInfo(ExecutionSession &ES, |
98 | const object::ELFObjectFileBase &Obj) { |
99 | MaterializationUnit::Interface I; |
100 | |
101 | for (auto &Sym : Obj.symbols()) { |
102 | Expected<uint32_t> SymFlagsOrErr = Sym.getFlags(); |
103 | if (!SymFlagsOrErr) |
104 | // TODO: Test this error. |
105 | return SymFlagsOrErr.takeError(); |
106 | |
107 | // Skip symbols not defined in this object file. |
108 | if (*SymFlagsOrErr & object::BasicSymbolRef::SF_Undefined) |
109 | continue; |
110 | |
111 | // Skip symbols that are not global. |
112 | if (!(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global)) |
113 | continue; |
114 | |
115 | // Skip symbols that have type SF_File. |
116 | if (auto SymType = Sym.getType()) { |
117 | if (*SymType == object::SymbolRef::ST_File) |
118 | continue; |
119 | } else |
120 | return SymType.takeError(); |
121 | |
122 | auto Name = Sym.getName(); |
123 | if (!Name) |
124 | return Name.takeError(); |
125 | |
126 | auto SymFlags = JITSymbolFlags::fromObjectSymbol(Symbol: Sym); |
127 | if (!SymFlags) |
128 | return SymFlags.takeError(); |
129 | |
130 | // ELF STB_GNU_UNIQUE should map to Weak for ORC. |
131 | if (Sym.getBinding() == ELF::STB_GNU_UNIQUE) |
132 | *SymFlags |= JITSymbolFlags::Weak; |
133 | |
134 | I.SymbolFlags[ES.intern(SymName: std::move(*Name))] = std::move(*SymFlags); |
135 | } |
136 | |
137 | SymbolStringPtr InitSymbol; |
138 | for (auto &Sec : Obj.sections()) { |
139 | if (auto SecName = Sec.getName()) { |
140 | if (isELFInitializerSection(SecName: *SecName)) { |
141 | addInitSymbol(I, ES, ObjFileName: Obj.getFileName()); |
142 | break; |
143 | } |
144 | } |
145 | } |
146 | |
147 | return I; |
148 | } |
149 | |
150 | static Expected<MaterializationUnit::Interface> |
151 | getCOFFObjectFileSymbolInfo(ExecutionSession &ES, |
152 | const object::COFFObjectFile &Obj) { |
153 | MaterializationUnit::Interface I; |
154 | std::vector<std::optional<object::coff_aux_section_definition>> ComdatDefs( |
155 | Obj.getNumberOfSections() + 1); |
156 | for (auto &Sym : Obj.symbols()) { |
157 | Expected<uint32_t> SymFlagsOrErr = Sym.getFlags(); |
158 | if (!SymFlagsOrErr) |
159 | // TODO: Test this error. |
160 | return SymFlagsOrErr.takeError(); |
161 | |
162 | // Handle comdat symbols |
163 | auto COFFSym = Obj.getCOFFSymbol(Symbol: Sym); |
164 | bool IsWeak = false; |
165 | if (auto *Def = COFFSym.getSectionDefinition()) { |
166 | auto Sec = Obj.getSection(index: COFFSym.getSectionNumber()); |
167 | if (!Sec) |
168 | return Sec.takeError(); |
169 | if (((*Sec)->Characteristics & COFF::IMAGE_SCN_LNK_COMDAT) && |
170 | Def->Selection != COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { |
171 | ComdatDefs[COFFSym.getSectionNumber()] = *Def; |
172 | continue; |
173 | } |
174 | } |
175 | if (!COFF::isReservedSectionNumber(SectionNumber: COFFSym.getSectionNumber()) && |
176 | ComdatDefs[COFFSym.getSectionNumber()]) { |
177 | auto Def = ComdatDefs[COFFSym.getSectionNumber()]; |
178 | if (Def->Selection != COFF::IMAGE_COMDAT_SELECT_NODUPLICATES) { |
179 | IsWeak = true; |
180 | } |
181 | ComdatDefs[COFFSym.getSectionNumber()] = std::nullopt; |
182 | } else { |
183 | // Skip symbols not defined in this object file. |
184 | if (*SymFlagsOrErr & object::BasicSymbolRef::SF_Undefined) |
185 | continue; |
186 | } |
187 | |
188 | // Skip symbols that are not global. |
189 | if (!(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global)) |
190 | continue; |
191 | |
192 | // Skip symbols that have type SF_File. |
193 | if (auto SymType = Sym.getType()) { |
194 | if (*SymType == object::SymbolRef::ST_File) |
195 | continue; |
196 | } else |
197 | return SymType.takeError(); |
198 | |
199 | auto Name = Sym.getName(); |
200 | if (!Name) |
201 | return Name.takeError(); |
202 | |
203 | auto SymFlags = JITSymbolFlags::fromObjectSymbol(Symbol: Sym); |
204 | if (!SymFlags) |
205 | return SymFlags.takeError(); |
206 | *SymFlags |= JITSymbolFlags::Exported; |
207 | |
208 | // Weak external is always a function |
209 | if (COFFSym.isWeakExternal()) |
210 | *SymFlags |= JITSymbolFlags::Callable; |
211 | |
212 | if (IsWeak) |
213 | *SymFlags |= JITSymbolFlags::Weak; |
214 | |
215 | I.SymbolFlags[ES.intern(SymName: *Name)] = std::move(*SymFlags); |
216 | } |
217 | |
218 | SymbolStringPtr InitSymbol; |
219 | for (auto &Sec : Obj.sections()) { |
220 | if (auto SecName = Sec.getName()) { |
221 | if (isCOFFInitializerSection(Name: *SecName)) { |
222 | addInitSymbol(I, ES, ObjFileName: Obj.getFileName()); |
223 | break; |
224 | } |
225 | } else |
226 | return SecName.takeError(); |
227 | } |
228 | |
229 | return I; |
230 | } |
231 | |
232 | Expected<MaterializationUnit::Interface> |
233 | getXCOFFObjectFileSymbolInfo(ExecutionSession &ES, |
234 | const object::ObjectFile &Obj) { |
235 | |
236 | MaterializationUnit::Interface I; |
237 | |
238 | for (auto &Sym : Obj.symbols()) { |
239 | Expected<uint32_t> SymFlagsOrErr = Sym.getFlags(); |
240 | if (!SymFlagsOrErr) |
241 | return SymFlagsOrErr.takeError(); |
242 | uint32_t Flags = *SymFlagsOrErr; |
243 | |
244 | // Skip undefined, non global and ST_File |
245 | if (Flags & object::SymbolRef::SF_Undefined) |
246 | continue; |
247 | if (!(Flags & object::SymbolRef::SF_Global)) |
248 | continue; |
249 | |
250 | auto SymbolType = Sym.getType(); |
251 | if (!SymbolType) |
252 | return SymbolType.takeError(); |
253 | |
254 | if (*SymbolType == object::SymbolRef::ST_File) |
255 | continue; |
256 | |
257 | auto Name = Sym.getName(); |
258 | if (!Name) |
259 | return Name.takeError(); |
260 | auto SymFlags = JITSymbolFlags::fromObjectSymbol(Symbol: Sym); |
261 | if (!SymFlags) |
262 | return SymFlags.takeError(); |
263 | |
264 | // TODO: Revisit symbol visibility |
265 | // On AIX, symbols with C_EXT and C_WEAKEXT symbols have no specified |
266 | // visibility are considered to have Default scope for LinkGraph. When the |
267 | // object is not a DSO, symbol visibility is not specified. In the absence |
268 | // of an Export List, its reasonable to minimic roughly the behaviour of |
269 | // -bexpall or CreateExportList. |
270 | *SymFlags |= JITSymbolFlags::Exported; |
271 | |
272 | I.SymbolFlags[ES.intern(SymName: std::move(*Name))] = std::move(*SymFlags); |
273 | } |
274 | // TODO: Find all initialization symbols for c++ static initializers |
275 | return I; |
276 | } |
277 | |
278 | Expected<MaterializationUnit::Interface> |
279 | getGenericObjectFileSymbolInfo(ExecutionSession &ES, |
280 | const object::ObjectFile &Obj) { |
281 | MaterializationUnit::Interface I; |
282 | |
283 | for (auto &Sym : Obj.symbols()) { |
284 | Expected<uint32_t> SymFlagsOrErr = Sym.getFlags(); |
285 | if (!SymFlagsOrErr) |
286 | // TODO: Test this error. |
287 | return SymFlagsOrErr.takeError(); |
288 | |
289 | // Skip symbols not defined in this object file. |
290 | if (*SymFlagsOrErr & object::BasicSymbolRef::SF_Undefined) |
291 | continue; |
292 | |
293 | // Skip symbols that are not global. |
294 | if (!(*SymFlagsOrErr & object::BasicSymbolRef::SF_Global)) |
295 | continue; |
296 | |
297 | // Skip symbols that have type SF_File. |
298 | if (auto SymType = Sym.getType()) { |
299 | if (*SymType == object::SymbolRef::ST_File) |
300 | continue; |
301 | } else |
302 | return SymType.takeError(); |
303 | |
304 | auto Name = Sym.getName(); |
305 | if (!Name) |
306 | return Name.takeError(); |
307 | |
308 | auto SymFlags = JITSymbolFlags::fromObjectSymbol(Symbol: Sym); |
309 | if (!SymFlags) |
310 | return SymFlags.takeError(); |
311 | |
312 | I.SymbolFlags[ES.intern(SymName: *Name)] = std::move(*SymFlags); |
313 | } |
314 | |
315 | return I; |
316 | } |
317 | |
318 | Expected<MaterializationUnit::Interface> |
319 | getObjectFileInterface(ExecutionSession &ES, MemoryBufferRef ObjBuffer) { |
320 | auto Obj = object::ObjectFile::createObjectFile(Object: ObjBuffer); |
321 | |
322 | if (!Obj) |
323 | return Obj.takeError(); |
324 | |
325 | if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(Val: Obj->get())) |
326 | return getMachOObjectFileSymbolInfo(ES, Obj: *MachOObj); |
327 | else if (auto *ELFObj = dyn_cast<object::ELFObjectFileBase>(Val: Obj->get())) |
328 | return getELFObjectFileSymbolInfo(ES, Obj: *ELFObj); |
329 | else if (auto *COFFObj = dyn_cast<object::COFFObjectFile>(Val: Obj->get())) |
330 | return getCOFFObjectFileSymbolInfo(ES, Obj: *COFFObj); |
331 | else if (auto *XCOFFObj = dyn_cast<object::XCOFFObjectFile>(Val: Obj->get())) |
332 | return getXCOFFObjectFileSymbolInfo(ES, Obj: *XCOFFObj); |
333 | |
334 | return getGenericObjectFileSymbolInfo(ES, Obj: **Obj); |
335 | } |
336 | |
337 | } // End namespace orc. |
338 | } // End namespace llvm. |
339 | |