LLVM 23.0.0git
ArchiveLinker.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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// This file implements shared functionality for linking static libraries
10// (archives) in offloading tools.
11//
12//===----------------------------------------------------------------------===//
13
15#include "llvm/ADT/STLExtras.h"
17#include "llvm/Object/Archive.h"
20#include "llvm/Support/Error.h"
23#include "llvm/Support/Path.h"
24#include <optional>
25#include <string>
26
27using namespace llvm;
28using namespace llvm::object;
29
30namespace llvm {
31namespace offloading {
32
34 const SymbolRef &Sym) {
35 Symbol Result;
36 Result.File = File;
37
38 auto FlagsOrErr = Sym.getFlags();
39 if (!FlagsOrErr)
40 return FlagsOrErr.takeError();
41
42 if (*FlagsOrErr & SymbolRef::SF_Undefined)
43 Result.SymFlags |= Undefined;
44 if (*FlagsOrErr & SymbolRef::SF_Weak)
45 Result.SymFlags |= Weak;
46
47 return Result;
48}
49
50static std::optional<std::string> findFile(StringRef Dir, StringRef Root,
51 const Twine &Name) {
53 if (Dir.starts_with("="))
54 sys::path::append(Path, Root, Dir.substr(1), Name);
55 else
56 sys::path::append(Path, Dir, Name);
57
58 if (sys::fs::exists(Path))
59 return static_cast<std::string>(Path);
60 return std::nullopt;
61}
62
63static std::optional<std::string>
65 ArrayRef<StringRef> SearchPaths) {
66 for (StringRef Dir : SearchPaths)
67 if (std::optional<std::string> File = findFile(Dir, Root, Name))
68 return File;
69 return std::nullopt;
70}
71
72/// Search for static libraries in the linker's library path given input like
73/// `-lfoo` or `-l:libfoo.a`.
74static std::optional<std::string>
76 ArrayRef<StringRef> SearchPaths) {
77 if (Input.starts_with(":"))
78 return findFromSearchPaths(Input.drop_front(), Root, SearchPaths);
79 SmallString<128> LibName;
80 ("lib" + Input + ".a").toVector(LibName);
81 return findFromSearchPaths(LibName, Root, SearchPaths);
82}
83
85 StringMap<Symbol> &SymTab,
86 bool IsLazy) {
87 Expected<IRSymtabFile> IRSymtabOrErr = readIRSymtab(Buffer);
88 if (!IRSymtabOrErr)
89 return IRSymtabOrErr.takeError();
90 bool Extracted = !IsLazy;
91 StringMap<Symbol> PendingSymbols;
92 for (unsigned I = 0; I != IRSymtabOrErr->Mods.size(); ++I) {
93 for (const auto &IRSym : IRSymtabOrErr->TheReader.module_symbols(I)) {
94 if (IRSym.isFormatSpecific() || !IRSym.isGlobal())
95 continue;
96
98 (IsLazy && !SymTab.count(IRSym.getName())) ? PendingSymbols : SymTab;
99 Symbol &OldSym = Target[IRSym.getName()];
100 Symbol Sym = Symbol(Buffer, IRSym);
101 if (OldSym.SymFlags == Symbol::None)
102 OldSym = Sym;
103
104 bool ResolvesReference =
105 !Sym.isUndefined() &&
106 (OldSym.isUndefined() || (OldSym.isWeak() && !Sym.isWeak())) &&
107 !(OldSym.isWeak() && OldSym.isUndefined() && IsLazy);
108 Extracted |= ResolvesReference;
109
111 if (ResolvesReference)
112 OldSym = Sym;
113 }
114 }
115 if (Extracted)
116 for (const auto &[Name, Symbol] : PendingSymbols)
117 SymTab[Name] = Symbol;
118 return Extracted;
119}
120
122 StringMap<Symbol> &SymTab,
123 bool IsLazy) {
124 bool Extracted = !IsLazy;
125 StringMap<Symbol> PendingSymbols;
126 for (SymbolRef ObjSym : ObjFile.symbols()) {
127 auto NameOrErr = ObjSym.getName();
128 if (!NameOrErr)
129 return NameOrErr.takeError();
130
132 (IsLazy && !SymTab.count(*NameOrErr)) ? PendingSymbols : SymTab;
133 Symbol &OldSym = Target[*NameOrErr];
134
135 auto SymOrErr =
137 if (!SymOrErr)
138 return SymOrErr.takeError();
139 Symbol Sym = *SymOrErr;
140
141 if (OldSym.SymFlags == Symbol::None)
142 OldSym = Sym;
143
144 bool ResolvesReference = OldSym.isUndefined() && !Sym.isUndefined() &&
145 (!OldSym.isWeak() || !IsLazy);
146 Extracted |= ResolvesReference;
147
148 if (ResolvesReference)
149 OldSym = Sym;
150 OldSym.UsedInRegularObj = true;
151 }
152 if (Extracted)
153 for (const auto &[Name, Symbol] : PendingSymbols)
154 SymTab[Name] = Symbol;
155 return Extracted;
156}
157
158/// Identify "fat binary" inputs that should be passed through to the linker
159/// without symbol-driven extraction. An input is a fat binary if \p DeviceArchs
160/// is non-empty and the input is an ELF object whose architecture is not one of
161/// the device architectures (or which fails to parse as an object file).
162static bool isFatBinary(MemoryBufferRef Buffer,
163 ArrayRef<Triple::ArchType> DeviceArchs) {
164 if (DeviceArchs.empty())
165 return false;
167 return false;
170 if (!ObjFile) {
171 // Assume fat binary if the object creation fails.
172 consumeError(ObjFile.takeError());
173 return true;
174 }
175 return !llvm::is_contained(DeviceArchs, (*ObjFile)->getArch());
176}
177
179 StringMap<Symbol> &SymTab, bool IsLazy) {
180 switch (identify_magic(Buffer.getBuffer())) {
181 case file_magic::bitcode: {
182 return getSymbolsFromBitcode(Buffer, SymTab, IsLazy);
183 }
187 if (!ObjFile)
188 return ObjFile.takeError();
189 return getSymbolsFromObject(**ObjFile, SymTab, IsLazy);
190 }
191 default:
192 return createStringError("Unsupported file type: '" +
193 Buffer.getBufferIdentifier() + "'");
194 }
195}
196
199 ArrayRef<StringRef> SearchPaths,
200 ArrayRef<StringRef> ForcedUndefs, StringRef Root,
201 ArrayRef<Triple::ArchType> DeviceArchs) {
202 ResolvedInputs Result;
204
205 // Process each input descriptor.
206 for (const InputDesc &Desc : Order) {
207 std::optional<std::string> Filename;
208
209 if (Desc.InputKind == InputDesc::Kind::Library) {
210 Filename = searchLibrary(Desc.Value, Root, SearchPaths);
211 if (!Filename)
212 return createStringError("unable to find library -l%s",
213 Desc.Value.str().c_str());
215 return createStringError("'%s': Is a directory", Filename->c_str());
216 } else {
217 if (!sys::fs::exists(Desc.Value))
218 return createStringError("input file not found: '" + Desc.Value + "'");
219 if (sys::fs::is_directory(Desc.Value))
220 return createStringError("'" + Desc.Value + "': Is a directory");
221 Filename = Desc.Value.str();
222 }
223
224 if (!Filename)
225 continue;
226
227 auto BufferOrErr =
229 if (!BufferOrErr)
230 return createFileError(*Filename, BufferOrErr.takeError());
231
232 MemoryBufferRef Buffer = (*BufferOrErr)->getMemBufferRef();
233 switch (identify_magic(Buffer.getBuffer())) {
236 InputFiles.emplace_back(std::move(*BufferOrErr), /*IsLazy=*/false);
237 break;
238 case file_magic::archive: {
241 if (!LibFile)
242 return LibFile.takeError();
243 Error Err = Error::success();
244 for (auto Child : (*LibFile)->children(Err)) {
245 auto ChildBufferOrErr = Child.getMemoryBufferRef();
246 if (!ChildBufferOrErr)
247 return ChildBufferOrErr.takeError();
248 // Include archive name in buffer identifier for better diagnostics.
249 std::string BufferIdentifier =
250 (*Filename + "(" + ChildBufferOrErr->getBufferIdentifier() + ")")
251 .str();
252 std::unique_ptr<MemoryBuffer> ChildBuffer =
253 MemoryBuffer::getMemBufferCopy(ChildBufferOrErr->getBuffer(),
254 BufferIdentifier);
255 InputFiles.emplace_back(std::move(ChildBuffer), !Desc.WholeArchive);
256 }
257 if (Err)
258 return Err;
259 break;
260 }
261 default:
262 return createStringError("Unsupported file type: '" + *Filename + "'");
263 }
264 }
265
266 // Seed symbol table with forced undefined symbols.
267 for (StringRef Sym : ForcedUndefs)
268 Result.SymTab[Sym] = Symbol(Symbol::Undefined);
269
270 // Fixed-point loop to extract archive members.
271 bool Extracted = true;
272 while (Extracted) {
273 Extracted = false;
274 for (auto &[Input, IsLazy] : InputFiles) {
275 if (!Input)
276 continue;
277
278 // Check if this is a fat binary that should be passed through.
279 if (isFatBinary(*Input, DeviceArchs)) {
280 Result.Buffers.emplace_back(std::move(Input));
281 continue;
282 }
283
284 // Archive members only extract if they define needed symbols.
285 Expected<bool> ExtractOrErr = getSymbols(*Input, Result.SymTab, IsLazy);
286 if (!ExtractOrErr)
287 return ExtractOrErr.takeError();
288
289 Extracted |= *ExtractOrErr;
290 if (!*ExtractOrErr)
291 continue;
292
293 Result.Buffers.emplace_back(std::move(Input));
294 }
295 }
296
297 return Result;
298}
299
300} // namespace offloading
301} // namespace llvm
#define I(x, y, z)
Definition MD5.cpp:57
static constexpr StringLiteral Filename
This file contains some templates that are useful if you are working with the STL at all.
The Input class is used to parse a yaml document into in-memory structs and vectors.
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
StringRef getBufferIdentifier() const
StringRef getBuffer() const
static std::unique_ptr< MemoryBuffer > getMemBufferCopy(StringRef InputData, const Twine &BufferName="")
Open the specified memory range as a MemoryBuffer, copying the contents and taking ownership of it.
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFileOrSTDIN(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
static Expected< OwningBinary< ObjectFile > > createObjectFile(StringRef ObjectPath)
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
reference emplace_back(ArgTypes &&... Args)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition StringMap.h:133
size_type count(StringRef Key) const
count - Return 1 if the element is in the map, 0 otherwise.
Definition StringMap.h:285
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
Definition StringRef.h:591
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
Definition StringRef.h:258
Target - Wrapper for Target specific information.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
static Expected< std::unique_ptr< Archive > > create(MemoryBufferRef Source)
Definition Archive.cpp:785
Expected< uint32_t > getFlags() const
Get symbol flags (bitwise OR of SymbolRef::Flags)
MemoryBufferRef getMemoryBufferRef() const
Definition Binary.cpp:43
This class is the base class for all object file types.
Definition ObjectFile.h:231
symbol_iterator_range symbols() const
Definition ObjectFile.h:323
This is a value type class that represents a single symbol in the list of symbols in the object file.
Definition ObjectFile.h:170
LLVM_ABI Expected< IRSymtabFile > readIRSymtab(MemoryBufferRef MBRef)
Reads a bitcode file, creating its irsymtab if necessary.
Expected< ResolvedInputs > resolveArchiveMembers(ArrayRef< InputDesc > Order, ArrayRef< StringRef > SearchPaths, ArrayRef< StringRef > ForcedUndefs={}, StringRef Root="", ArrayRef< Triple::ArchType > DeviceArchs={})
Resolve archive members from the given inputs using a symbol-driven fixed-point algorithm.
static std::optional< std::string > findFromSearchPaths(StringRef Name, StringRef Root, ArrayRef< StringRef > SearchPaths)
static std::optional< std::string > findFile(StringRef Dir, StringRef Root, const Twine &Name)
static Expected< bool > getSymbols(MemoryBufferRef Buffer, StringMap< Symbol > &SymTab, bool IsLazy)
static bool isFatBinary(MemoryBufferRef Buffer, ArrayRef< Triple::ArchType > DeviceArchs)
Identify "fat binary" inputs that should be passed through to the linker without symbol-driven extrac...
static Expected< bool > getSymbolsFromBitcode(MemoryBufferRef Buffer, StringMap< Symbol > &SymTab, bool IsLazy)
static std::optional< std::string > searchLibrary(StringRef Input, StringRef Root, ArrayRef< StringRef > SearchPaths)
Search for static libraries in the linker's library path given input like -lfoo or -l:libfoo....
static Expected< bool > getSymbolsFromObject(ObjectFile &ObjFile, StringMap< Symbol > &SymTab, bool IsLazy)
LLVM_ABI bool exists(const basic_file_status &status)
Does file exist?
Definition Path.cpp:1107
LLVM_ABI bool is_directory(const basic_file_status &status)
Does status represent a directory?
Definition Path.cpp:1122
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:467
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI file_magic identify_magic(StringRef magic)
Identify the type of a binary file based on how magical it is.
Definition Magic.cpp:33
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
Definition Error.h:1415
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1321
Op::Description Desc
Expected< T > errorOrToExpected(ErrorOr< T > &&EO)
Convert an ErrorOr<T> to an Expected<T>.
Definition Error.h:1261
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1946
void consumeError(Error Err)
Consume a Error without doing anything.
Definition Error.h:1106
@ elf_relocatable
ELF Relocatable object file.
Definition Magic.h:28
@ archive
ar style archive file
Definition Magic.h:26
@ bitcode
Bitcode file.
Definition Magic.h:24
Description of a single input (file or library).
Result of archive member resolution.
A minimum symbol interface that provides the necessary information to extract archive members and res...
static Expected< Symbol > createFromObject(MemoryBufferRef File, const object::SymbolRef &Sym)
Create a Symbol from an object file symbol reference.