1 | //===- ArchiveWriter.cpp - ar File Format implementation --------*- C++ -*-===// |
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 defines the writeArchive function. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/Object/ArchiveWriter.h" |
14 | #include "llvm/ADT/ArrayRef.h" |
15 | #include "llvm/ADT/StringMap.h" |
16 | #include "llvm/ADT/StringRef.h" |
17 | #include "llvm/BinaryFormat/Magic.h" |
18 | #include "llvm/IR/LLVMContext.h" |
19 | #include "llvm/Object/Archive.h" |
20 | #include "llvm/Object/COFF.h" |
21 | #include "llvm/Object/COFFImportFile.h" |
22 | #include "llvm/Object/Error.h" |
23 | #include "llvm/Object/IRObjectFile.h" |
24 | #include "llvm/Object/MachO.h" |
25 | #include "llvm/Object/ObjectFile.h" |
26 | #include "llvm/Object/SymbolicFile.h" |
27 | #include "llvm/Object/XCOFFObjectFile.h" |
28 | #include "llvm/Support/Alignment.h" |
29 | #include "llvm/Support/EndianStream.h" |
30 | #include "llvm/Support/Errc.h" |
31 | #include "llvm/Support/ErrorHandling.h" |
32 | #include "llvm/Support/Format.h" |
33 | #include "llvm/Support/MathExtras.h" |
34 | #include "llvm/Support/Path.h" |
35 | #include "llvm/Support/SmallVectorMemoryBuffer.h" |
36 | #include "llvm/Support/raw_ostream.h" |
37 | |
38 | #include <cerrno> |
39 | #include <map> |
40 | |
41 | #if !defined(_MSC_VER) && !defined(__MINGW32__) |
42 | #include <unistd.h> |
43 | #else |
44 | #include <io.h> |
45 | #endif |
46 | |
47 | using namespace llvm; |
48 | using namespace llvm::object; |
49 | |
50 | struct SymMap { |
51 | bool UseECMap = false; |
52 | std::map<std::string, uint16_t> Map; |
53 | std::map<std::string, uint16_t> ECMap; |
54 | }; |
55 | |
56 | NewArchiveMember::NewArchiveMember(MemoryBufferRef BufRef) |
57 | : Buf(MemoryBuffer::getMemBuffer(Ref: BufRef, RequiresNullTerminator: false)), |
58 | MemberName(BufRef.getBufferIdentifier()) {} |
59 | |
60 | object::Archive::Kind NewArchiveMember::detectKindFromObject() const { |
61 | auto MemBufferRef = this->Buf->getMemBufferRef(); |
62 | Expected<std::unique_ptr<object::ObjectFile>> OptionalObject = |
63 | object::ObjectFile::createObjectFile(Object: MemBufferRef); |
64 | |
65 | if (OptionalObject) { |
66 | if (isa<object::MachOObjectFile>(Val: **OptionalObject)) |
67 | return object::Archive::K_DARWIN; |
68 | if (isa<object::XCOFFObjectFile>(Val: **OptionalObject)) |
69 | return object::Archive::K_AIXBIG; |
70 | if (isa<object::COFFObjectFile>(Val: **OptionalObject) || |
71 | isa<object::COFFImportFile>(Val: **OptionalObject)) |
72 | return object::Archive::K_COFF; |
73 | return object::Archive::K_GNU; |
74 | } |
75 | |
76 | // Squelch the error in case we had a non-object file. |
77 | consumeError(Err: OptionalObject.takeError()); |
78 | |
79 | // If we're adding a bitcode file to the archive, detect the Archive kind |
80 | // based on the target triple. |
81 | LLVMContext Context; |
82 | if (identify_magic(magic: MemBufferRef.getBuffer()) == file_magic::bitcode) { |
83 | if (auto ObjOrErr = object::SymbolicFile::createSymbolicFile( |
84 | Object: MemBufferRef, Type: file_magic::bitcode, Context: &Context)) { |
85 | auto &IRObject = cast<object::IRObjectFile>(Val&: **ObjOrErr); |
86 | auto TargetTriple = Triple(IRObject.getTargetTriple()); |
87 | return object::Archive::getDefaultKindForTriple(T&: TargetTriple); |
88 | } else { |
89 | // Squelch the error in case this was not a SymbolicFile. |
90 | consumeError(Err: ObjOrErr.takeError()); |
91 | } |
92 | } |
93 | |
94 | return object::Archive::getDefaultKind(); |
95 | } |
96 | |
97 | Expected<NewArchiveMember> |
98 | NewArchiveMember::getOldMember(const object::Archive::Child &OldMember, |
99 | bool Deterministic) { |
100 | Expected<llvm::MemoryBufferRef> BufOrErr = OldMember.getMemoryBufferRef(); |
101 | if (!BufOrErr) |
102 | return BufOrErr.takeError(); |
103 | |
104 | NewArchiveMember M; |
105 | M.Buf = MemoryBuffer::getMemBuffer(Ref: *BufOrErr, RequiresNullTerminator: false); |
106 | M.MemberName = M.Buf->getBufferIdentifier(); |
107 | if (!Deterministic) { |
108 | auto ModTimeOrErr = OldMember.getLastModified(); |
109 | if (!ModTimeOrErr) |
110 | return ModTimeOrErr.takeError(); |
111 | M.ModTime = ModTimeOrErr.get(); |
112 | Expected<unsigned> UIDOrErr = OldMember.getUID(); |
113 | if (!UIDOrErr) |
114 | return UIDOrErr.takeError(); |
115 | M.UID = UIDOrErr.get(); |
116 | Expected<unsigned> GIDOrErr = OldMember.getGID(); |
117 | if (!GIDOrErr) |
118 | return GIDOrErr.takeError(); |
119 | M.GID = GIDOrErr.get(); |
120 | Expected<sys::fs::perms> AccessModeOrErr = OldMember.getAccessMode(); |
121 | if (!AccessModeOrErr) |
122 | return AccessModeOrErr.takeError(); |
123 | M.Perms = AccessModeOrErr.get(); |
124 | } |
125 | return std::move(M); |
126 | } |
127 | |
128 | Expected<NewArchiveMember> NewArchiveMember::getFile(StringRef FileName, |
129 | bool Deterministic) { |
130 | sys::fs::file_status Status; |
131 | auto FDOrErr = sys::fs::openNativeFileForRead(Name: FileName); |
132 | if (!FDOrErr) |
133 | return FDOrErr.takeError(); |
134 | sys::fs::file_t FD = *FDOrErr; |
135 | assert(FD != sys::fs::kInvalidFile); |
136 | |
137 | if (auto EC = sys::fs::status(FD, Result&: Status)) |
138 | return errorCodeToError(EC); |
139 | |
140 | // Opening a directory doesn't make sense. Let it fail. |
141 | // Linux cannot open directories with open(2), although |
142 | // cygwin and *bsd can. |
143 | if (Status.type() == sys::fs::file_type::directory_file) |
144 | return errorCodeToError(EC: make_error_code(E: errc::is_a_directory)); |
145 | |
146 | ErrorOr<std::unique_ptr<MemoryBuffer>> MemberBufferOrErr = |
147 | MemoryBuffer::getOpenFile(FD, Filename: FileName, FileSize: Status.getSize(), RequiresNullTerminator: false); |
148 | if (!MemberBufferOrErr) |
149 | return errorCodeToError(EC: MemberBufferOrErr.getError()); |
150 | |
151 | if (auto EC = sys::fs::closeFile(F&: FD)) |
152 | return errorCodeToError(EC); |
153 | |
154 | NewArchiveMember M; |
155 | M.Buf = std::move(*MemberBufferOrErr); |
156 | M.MemberName = M.Buf->getBufferIdentifier(); |
157 | if (!Deterministic) { |
158 | M.ModTime = std::chrono::time_point_cast<std::chrono::seconds>( |
159 | t: Status.getLastModificationTime()); |
160 | M.UID = Status.getUser(); |
161 | M.GID = Status.getGroup(); |
162 | M.Perms = Status.permissions(); |
163 | } |
164 | return std::move(M); |
165 | } |
166 | |
167 | template <typename T> |
168 | static void printWithSpacePadding(raw_ostream &OS, T Data, unsigned Size) { |
169 | uint64_t OldPos = OS.tell(); |
170 | OS << Data; |
171 | unsigned SizeSoFar = OS.tell() - OldPos; |
172 | assert(SizeSoFar <= Size && "Data doesn't fit in Size" ); |
173 | OS.indent(NumSpaces: Size - SizeSoFar); |
174 | } |
175 | |
176 | static bool isDarwin(object::Archive::Kind Kind) { |
177 | return Kind == object::Archive::K_DARWIN || |
178 | Kind == object::Archive::K_DARWIN64; |
179 | } |
180 | |
181 | static bool isAIXBigArchive(object::Archive::Kind Kind) { |
182 | return Kind == object::Archive::K_AIXBIG; |
183 | } |
184 | |
185 | static bool isCOFFArchive(object::Archive::Kind Kind) { |
186 | return Kind == object::Archive::K_COFF; |
187 | } |
188 | |
189 | static bool isBSDLike(object::Archive::Kind Kind) { |
190 | switch (Kind) { |
191 | case object::Archive::K_GNU: |
192 | case object::Archive::K_GNU64: |
193 | case object::Archive::K_AIXBIG: |
194 | case object::Archive::K_COFF: |
195 | return false; |
196 | case object::Archive::K_BSD: |
197 | case object::Archive::K_DARWIN: |
198 | case object::Archive::K_DARWIN64: |
199 | return true; |
200 | } |
201 | llvm_unreachable("not supported for writting" ); |
202 | } |
203 | |
204 | template <class T> |
205 | static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val) { |
206 | support::endian::write(Out, Val, |
207 | isBSDLike(Kind) ? llvm::endianness::little |
208 | : llvm::endianness::big); |
209 | } |
210 | |
211 | template <class T> static void printLE(raw_ostream &Out, T Val) { |
212 | support::endian::write(Out, Val, llvm::endianness::little); |
213 | } |
214 | |
215 | static void ( |
216 | raw_ostream &Out, const sys::TimePoint<std::chrono::seconds> &ModTime, |
217 | unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) { |
218 | printWithSpacePadding(OS&: Out, Data: sys::toTimeT(TP: ModTime), Size: 12); |
219 | |
220 | // The format has only 6 chars for uid and gid. Truncate if the provided |
221 | // values don't fit. |
222 | printWithSpacePadding(OS&: Out, Data: UID % 1000000, Size: 6); |
223 | printWithSpacePadding(OS&: Out, Data: GID % 1000000, Size: 6); |
224 | |
225 | printWithSpacePadding(OS&: Out, Data: format(Fmt: "%o" , Vals: Perms), Size: 8); |
226 | printWithSpacePadding(OS&: Out, Data: Size, Size: 10); |
227 | Out << "`\n" ; |
228 | } |
229 | |
230 | static void |
231 | (raw_ostream &Out, StringRef Name, |
232 | const sys::TimePoint<std::chrono::seconds> &ModTime, |
233 | unsigned UID, unsigned GID, unsigned Perms, |
234 | uint64_t Size) { |
235 | printWithSpacePadding(OS&: Out, Data: Twine(Name) + "/" , Size: 16); |
236 | printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); |
237 | } |
238 | |
239 | static void |
240 | (raw_ostream &Out, uint64_t Pos, StringRef Name, |
241 | const sys::TimePoint<std::chrono::seconds> &ModTime, |
242 | unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) { |
243 | uint64_t = Pos + 60 + Name.size(); |
244 | // Pad so that even 64 bit object files are aligned. |
245 | unsigned Pad = offsetToAlignment(Value: PosAfterHeader, Alignment: Align(8)); |
246 | unsigned NameWithPadding = Name.size() + Pad; |
247 | printWithSpacePadding(OS&: Out, Data: Twine("#1/" ) + Twine(NameWithPadding), Size: 16); |
248 | printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, |
249 | Size: NameWithPadding + Size); |
250 | Out << Name; |
251 | while (Pad--) |
252 | Out.write(C: uint8_t(0)); |
253 | } |
254 | |
255 | static void |
256 | (raw_ostream &Out, StringRef Name, |
257 | const sys::TimePoint<std::chrono::seconds> &ModTime, |
258 | unsigned UID, unsigned GID, unsigned Perms, |
259 | uint64_t Size, uint64_t PrevOffset, |
260 | uint64_t NextOffset) { |
261 | unsigned NameLen = Name.size(); |
262 | |
263 | printWithSpacePadding(OS&: Out, Data: Size, Size: 20); // File member size |
264 | printWithSpacePadding(OS&: Out, Data: NextOffset, Size: 20); // Next member header offset |
265 | printWithSpacePadding(OS&: Out, Data: PrevOffset, Size: 20); // Previous member header offset |
266 | printWithSpacePadding(OS&: Out, Data: sys::toTimeT(TP: ModTime), Size: 12); // File member date |
267 | // The big archive format has 12 chars for uid and gid. |
268 | printWithSpacePadding(OS&: Out, Data: UID % 1000000000000, Size: 12); // UID |
269 | printWithSpacePadding(OS&: Out, Data: GID % 1000000000000, Size: 12); // GID |
270 | printWithSpacePadding(OS&: Out, Data: format(Fmt: "%o" , Vals: Perms), Size: 12); // Permission |
271 | printWithSpacePadding(OS&: Out, Data: NameLen, Size: 4); // Name length |
272 | if (NameLen) { |
273 | printWithSpacePadding(OS&: Out, Data: Name, Size: NameLen); // Name |
274 | if (NameLen % 2) |
275 | Out.write(C: uint8_t(0)); // Null byte padding |
276 | } |
277 | Out << "`\n" ; // Terminator |
278 | } |
279 | |
280 | static bool useStringTable(bool Thin, StringRef Name) { |
281 | return Thin || Name.size() >= 16 || Name.contains(C: '/'); |
282 | } |
283 | |
284 | static bool is64BitKind(object::Archive::Kind Kind) { |
285 | switch (Kind) { |
286 | case object::Archive::K_GNU: |
287 | case object::Archive::K_BSD: |
288 | case object::Archive::K_DARWIN: |
289 | case object::Archive::K_COFF: |
290 | return false; |
291 | case object::Archive::K_AIXBIG: |
292 | case object::Archive::K_DARWIN64: |
293 | case object::Archive::K_GNU64: |
294 | return true; |
295 | } |
296 | llvm_unreachable("not supported for writting" ); |
297 | } |
298 | |
299 | static void |
300 | (raw_ostream &Out, uint64_t Pos, raw_ostream &StringTable, |
301 | StringMap<uint64_t> &MemberNames, object::Archive::Kind Kind, |
302 | bool Thin, const NewArchiveMember &M, |
303 | sys::TimePoint<std::chrono::seconds> ModTime, uint64_t Size) { |
304 | if (isBSDLike(Kind)) |
305 | return printBSDMemberHeader(Out, Pos, Name: M.MemberName, ModTime, UID: M.UID, GID: M.GID, |
306 | Perms: M.Perms, Size); |
307 | if (!useStringTable(Thin, Name: M.MemberName)) |
308 | return printGNUSmallMemberHeader(Out, Name: M.MemberName, ModTime, UID: M.UID, GID: M.GID, |
309 | Perms: M.Perms, Size); |
310 | Out << '/'; |
311 | uint64_t NamePos; |
312 | if (Thin) { |
313 | NamePos = StringTable.tell(); |
314 | StringTable << M.MemberName << "/\n" ; |
315 | } else { |
316 | auto Insertion = MemberNames.insert(KV: {M.MemberName, uint64_t(0)}); |
317 | if (Insertion.second) { |
318 | Insertion.first->second = StringTable.tell(); |
319 | StringTable << M.MemberName; |
320 | if (isCOFFArchive(Kind)) |
321 | StringTable << '\0'; |
322 | else |
323 | StringTable << "/\n" ; |
324 | } |
325 | NamePos = Insertion.first->second; |
326 | } |
327 | printWithSpacePadding(OS&: Out, Data: NamePos, Size: 15); |
328 | printRestOfMemberHeader(Out, ModTime, UID: M.UID, GID: M.GID, Perms: M.Perms, Size); |
329 | } |
330 | |
331 | namespace { |
332 | struct MemberData { |
333 | std::vector<unsigned> Symbols; |
334 | std::string ; |
335 | StringRef Data; |
336 | StringRef Padding; |
337 | uint64_t PreHeadPadSize = 0; |
338 | std::unique_ptr<SymbolicFile> SymFile = nullptr; |
339 | }; |
340 | } // namespace |
341 | |
342 | static MemberData computeStringTable(StringRef Names) { |
343 | unsigned Size = Names.size(); |
344 | unsigned Pad = offsetToAlignment(Value: Size, Alignment: Align(2)); |
345 | std::string ; |
346 | raw_string_ostream Out(Header); |
347 | printWithSpacePadding(OS&: Out, Data: "//" , Size: 48); |
348 | printWithSpacePadding(OS&: Out, Data: Size + Pad, Size: 10); |
349 | Out << "`\n" ; |
350 | Out.flush(); |
351 | return {.Symbols: {}, .Header: std::move(Header), .Data: Names, .Padding: Pad ? "\n" : "" }; |
352 | } |
353 | |
354 | static sys::TimePoint<std::chrono::seconds> now(bool Deterministic) { |
355 | using namespace std::chrono; |
356 | |
357 | if (!Deterministic) |
358 | return time_point_cast<seconds>(t: system_clock::now()); |
359 | return sys::TimePoint<seconds>(); |
360 | } |
361 | |
362 | static bool isArchiveSymbol(const object::BasicSymbolRef &S) { |
363 | Expected<uint32_t> SymFlagsOrErr = S.getFlags(); |
364 | if (!SymFlagsOrErr) |
365 | // TODO: Actually report errors helpfully. |
366 | report_fatal_error(Err: SymFlagsOrErr.takeError()); |
367 | if (*SymFlagsOrErr & object::SymbolRef::SF_FormatSpecific) |
368 | return false; |
369 | if (!(*SymFlagsOrErr & object::SymbolRef::SF_Global)) |
370 | return false; |
371 | if (*SymFlagsOrErr & object::SymbolRef::SF_Undefined) |
372 | return false; |
373 | return true; |
374 | } |
375 | |
376 | static void printNBits(raw_ostream &Out, object::Archive::Kind Kind, |
377 | uint64_t Val) { |
378 | if (is64BitKind(Kind)) |
379 | print<uint64_t>(Out, Kind, Val); |
380 | else |
381 | print<uint32_t>(Out, Kind, Val); |
382 | } |
383 | |
384 | static uint64_t computeSymbolTableSize(object::Archive::Kind Kind, |
385 | uint64_t NumSyms, uint64_t OffsetSize, |
386 | uint64_t StringTableSize, |
387 | uint32_t *Padding = nullptr) { |
388 | assert((OffsetSize == 4 || OffsetSize == 8) && "Unsupported OffsetSize" ); |
389 | uint64_t Size = OffsetSize; // Number of entries |
390 | if (isBSDLike(Kind)) |
391 | Size += NumSyms * OffsetSize * 2; // Table |
392 | else |
393 | Size += NumSyms * OffsetSize; // Table |
394 | if (isBSDLike(Kind)) |
395 | Size += OffsetSize; // byte count |
396 | Size += StringTableSize; |
397 | // ld64 expects the members to be 8-byte aligned for 64-bit content and at |
398 | // least 4-byte aligned for 32-bit content. Opt for the larger encoding |
399 | // uniformly. |
400 | // We do this for all bsd formats because it simplifies aligning members. |
401 | // For the big archive format, the symbol table is the last member, so there |
402 | // is no need to align. |
403 | uint32_t Pad = isAIXBigArchive(Kind) |
404 | ? 0 |
405 | : offsetToAlignment(Value: Size, Alignment: Align(isBSDLike(Kind) ? 8 : 2)); |
406 | |
407 | Size += Pad; |
408 | if (Padding) |
409 | *Padding = Pad; |
410 | return Size; |
411 | } |
412 | |
413 | static uint64_t computeSymbolMapSize(uint64_t NumObj, SymMap &SymMap, |
414 | uint32_t *Padding = nullptr) { |
415 | uint64_t Size = sizeof(uint32_t) * 2; // Number of symbols and objects entries |
416 | Size += NumObj * sizeof(uint32_t); // Offset table |
417 | |
418 | for (auto S : SymMap.Map) |
419 | Size += sizeof(uint16_t) + S.first.length() + 1; |
420 | |
421 | uint32_t Pad = offsetToAlignment(Value: Size, Alignment: Align(2)); |
422 | Size += Pad; |
423 | if (Padding) |
424 | *Padding = Pad; |
425 | return Size; |
426 | } |
427 | |
428 | static uint64_t computeECSymbolsSize(SymMap &SymMap, |
429 | uint32_t *Padding = nullptr) { |
430 | uint64_t Size = sizeof(uint32_t); // Number of symbols |
431 | |
432 | for (auto S : SymMap.ECMap) |
433 | Size += sizeof(uint16_t) + S.first.length() + 1; |
434 | |
435 | uint32_t Pad = offsetToAlignment(Value: Size, Alignment: Align(2)); |
436 | Size += Pad; |
437 | if (Padding) |
438 | *Padding = Pad; |
439 | return Size; |
440 | } |
441 | |
442 | static void (raw_ostream &Out, object::Archive::Kind Kind, |
443 | bool Deterministic, uint64_t Size, |
444 | uint64_t PrevMemberOffset = 0, |
445 | uint64_t NextMemberOffset = 0) { |
446 | if (isBSDLike(Kind)) { |
447 | const char *Name = is64BitKind(Kind) ? "__.SYMDEF_64" : "__.SYMDEF" ; |
448 | printBSDMemberHeader(Out, Pos: Out.tell(), Name, ModTime: now(Deterministic), UID: 0, GID: 0, Perms: 0, |
449 | Size); |
450 | } else if (isAIXBigArchive(Kind)) { |
451 | printBigArchiveMemberHeader(Out, Name: "" , ModTime: now(Deterministic), UID: 0, GID: 0, Perms: 0, Size, |
452 | PrevOffset: PrevMemberOffset, NextOffset: NextMemberOffset); |
453 | } else { |
454 | const char *Name = is64BitKind(Kind) ? "/SYM64" : "" ; |
455 | printGNUSmallMemberHeader(Out, Name, ModTime: now(Deterministic), UID: 0, GID: 0, Perms: 0, Size); |
456 | } |
457 | } |
458 | |
459 | static uint64_t (object::Archive::Kind Kind, |
460 | uint64_t NumMembers, |
461 | uint64_t StringMemberSize, uint64_t NumSyms, |
462 | uint64_t SymNamesSize, SymMap *SymMap) { |
463 | uint32_t OffsetSize = is64BitKind(Kind) ? 8 : 4; |
464 | uint64_t SymtabSize = |
465 | computeSymbolTableSize(Kind, NumSyms, OffsetSize, StringTableSize: SymNamesSize); |
466 | auto = [=] { |
467 | SmallString<0> TmpBuf; |
468 | raw_svector_ostream Tmp(TmpBuf); |
469 | writeSymbolTableHeader(Out&: Tmp, Kind, Deterministic: true, Size: SymtabSize); |
470 | return TmpBuf.size(); |
471 | }; |
472 | uint32_t = computeSymbolTableHeaderSize(); |
473 | uint64_t Size = strlen(s: "!<arch>\n" ) + HeaderSize + SymtabSize; |
474 | |
475 | if (SymMap) { |
476 | Size += HeaderSize + computeSymbolMapSize(NumObj: NumMembers, SymMap&: *SymMap); |
477 | if (SymMap->ECMap.size()) |
478 | Size += HeaderSize + computeECSymbolsSize(SymMap&: *SymMap); |
479 | } |
480 | |
481 | return Size + StringMemberSize; |
482 | } |
483 | |
484 | static Expected<std::unique_ptr<SymbolicFile>> |
485 | getSymbolicFile(MemoryBufferRef Buf, LLVMContext &Context) { |
486 | const file_magic Type = identify_magic(magic: Buf.getBuffer()); |
487 | // Don't attempt to read non-symbolic file types. |
488 | if (!object::SymbolicFile::isSymbolicFile(Type, Context: &Context)) |
489 | return nullptr; |
490 | if (Type == file_magic::bitcode) { |
491 | auto ObjOrErr = object::SymbolicFile::createSymbolicFile( |
492 | Object: Buf, Type: file_magic::bitcode, Context: &Context); |
493 | if (!ObjOrErr) |
494 | return ObjOrErr.takeError(); |
495 | return std::move(*ObjOrErr); |
496 | } else { |
497 | auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Object: Buf); |
498 | if (!ObjOrErr) |
499 | return ObjOrErr.takeError(); |
500 | return std::move(*ObjOrErr); |
501 | } |
502 | } |
503 | |
504 | static bool is64BitSymbolicFile(const SymbolicFile *SymObj) { |
505 | return SymObj != nullptr ? SymObj->is64Bit() : false; |
506 | } |
507 | |
508 | // Log2 of PAGESIZE(4096) on an AIX system. |
509 | static const uint32_t Log2OfAIXPageSize = 12; |
510 | |
511 | // In the AIX big archive format, since the data content follows the member file |
512 | // name, if the name ends on an odd byte, an extra byte will be added for |
513 | // padding. This ensures that the data within the member file starts at an even |
514 | // byte. |
515 | static const uint32_t MinBigArchiveMemDataAlign = 2; |
516 | |
517 | template <typename AuxiliaryHeader> |
518 | uint16_t getAuxMaxAlignment(uint16_t , AuxiliaryHeader *, |
519 | uint16_t Log2OfMaxAlign) { |
520 | // If the member doesn't have an auxiliary header, it isn't a loadable object |
521 | // and so it just needs aligning at the minimum value. |
522 | if (AuxHeader == nullptr) |
523 | return MinBigArchiveMemDataAlign; |
524 | |
525 | // If the auxiliary header does not have both MaxAlignOfData and |
526 | // MaxAlignOfText field, it is not a loadable shared object file, so align at |
527 | // the minimum value. The 'ModuleType' member is located right after |
528 | // 'MaxAlignOfData' in the AuxiliaryHeader. |
529 | if (AuxHeaderSize < offsetof(AuxiliaryHeader, ModuleType)) |
530 | return MinBigArchiveMemDataAlign; |
531 | |
532 | // If the XCOFF object file does not have a loader section, it is not |
533 | // loadable, so align at the minimum value. |
534 | if (AuxHeader->SecNumOfLoader == 0) |
535 | return MinBigArchiveMemDataAlign; |
536 | |
537 | // The content of the loadable member file needs to be aligned at MAX(maximum |
538 | // alignment of .text, maximum alignment of .data) if there are both fields. |
539 | // If the desired alignment is > PAGESIZE, 32-bit members are aligned on a |
540 | // word boundary, while 64-bit members are aligned on a PAGESIZE(2^12=4096) |
541 | // boundary. |
542 | uint16_t Log2OfAlign = |
543 | std::max(AuxHeader->MaxAlignOfText, AuxHeader->MaxAlignOfData); |
544 | return 1 << (Log2OfAlign > Log2OfAIXPageSize ? Log2OfMaxAlign : Log2OfAlign); |
545 | } |
546 | |
547 | // AIX big archives may contain shared object members. The AIX OS requires these |
548 | // members to be aligned if they are 64-bit and recommends it for 32-bit |
549 | // members. This ensures that when these members are loaded they are aligned in |
550 | // memory. |
551 | static uint32_t getMemberAlignment(SymbolicFile *SymObj) { |
552 | XCOFFObjectFile *XCOFFObj = dyn_cast_or_null<XCOFFObjectFile>(Val: SymObj); |
553 | if (!XCOFFObj) |
554 | return MinBigArchiveMemDataAlign; |
555 | |
556 | // If the desired alignment is > PAGESIZE, 32-bit members are aligned on a |
557 | // word boundary, while 64-bit members are aligned on a PAGESIZE boundary. |
558 | return XCOFFObj->is64Bit() |
559 | ? getAuxMaxAlignment(AuxHeaderSize: XCOFFObj->fileHeader64()->AuxHeaderSize, |
560 | AuxHeader: XCOFFObj->auxiliaryHeader64(), |
561 | Log2OfMaxAlign: Log2OfAIXPageSize) |
562 | : getAuxMaxAlignment(AuxHeaderSize: XCOFFObj->fileHeader32()->AuxHeaderSize, |
563 | AuxHeader: XCOFFObj->auxiliaryHeader32(), Log2OfMaxAlign: 2); |
564 | } |
565 | |
566 | static void writeSymbolTable(raw_ostream &Out, object::Archive::Kind Kind, |
567 | bool Deterministic, ArrayRef<MemberData> Members, |
568 | StringRef StringTable, uint64_t MembersOffset, |
569 | unsigned NumSyms, uint64_t PrevMemberOffset = 0, |
570 | uint64_t NextMemberOffset = 0, |
571 | bool Is64Bit = false) { |
572 | // We don't write a symbol table on an archive with no members -- except on |
573 | // Darwin, where the linker will abort unless the archive has a symbol table. |
574 | if (StringTable.empty() && !isDarwin(Kind) && !isCOFFArchive(Kind)) |
575 | return; |
576 | |
577 | uint64_t OffsetSize = is64BitKind(Kind) ? 8 : 4; |
578 | uint32_t Pad; |
579 | uint64_t Size = computeSymbolTableSize(Kind, NumSyms, OffsetSize, |
580 | StringTableSize: StringTable.size(), Padding: &Pad); |
581 | writeSymbolTableHeader(Out, Kind, Deterministic, Size, PrevMemberOffset, |
582 | NextMemberOffset); |
583 | |
584 | if (isBSDLike(Kind)) |
585 | printNBits(Out, Kind, Val: NumSyms * 2 * OffsetSize); |
586 | else |
587 | printNBits(Out, Kind, Val: NumSyms); |
588 | |
589 | uint64_t Pos = MembersOffset; |
590 | for (const MemberData &M : Members) { |
591 | if (isAIXBigArchive(Kind)) { |
592 | Pos += M.PreHeadPadSize; |
593 | if (is64BitSymbolicFile(SymObj: M.SymFile.get()) != Is64Bit) { |
594 | Pos += M.Header.size() + M.Data.size() + M.Padding.size(); |
595 | continue; |
596 | } |
597 | } |
598 | |
599 | for (unsigned StringOffset : M.Symbols) { |
600 | if (isBSDLike(Kind)) |
601 | printNBits(Out, Kind, Val: StringOffset); |
602 | printNBits(Out, Kind, Val: Pos); // member offset |
603 | } |
604 | Pos += M.Header.size() + M.Data.size() + M.Padding.size(); |
605 | } |
606 | |
607 | if (isBSDLike(Kind)) |
608 | // byte count of the string table |
609 | printNBits(Out, Kind, Val: StringTable.size()); |
610 | Out << StringTable; |
611 | |
612 | while (Pad--) |
613 | Out.write(C: uint8_t(0)); |
614 | } |
615 | |
616 | static void writeSymbolMap(raw_ostream &Out, object::Archive::Kind Kind, |
617 | bool Deterministic, ArrayRef<MemberData> Members, |
618 | SymMap &SymMap, uint64_t MembersOffset) { |
619 | uint32_t Pad; |
620 | uint64_t Size = computeSymbolMapSize(NumObj: Members.size(), SymMap, Padding: &Pad); |
621 | writeSymbolTableHeader(Out, Kind, Deterministic, Size, PrevMemberOffset: 0); |
622 | |
623 | uint32_t Pos = MembersOffset; |
624 | |
625 | printLE<uint32_t>(Out, Val: Members.size()); |
626 | for (const MemberData &M : Members) { |
627 | printLE(Out, Val: Pos); // member offset |
628 | Pos += M.Header.size() + M.Data.size() + M.Padding.size(); |
629 | } |
630 | |
631 | printLE<uint32_t>(Out, Val: SymMap.Map.size()); |
632 | |
633 | for (auto S : SymMap.Map) |
634 | printLE(Out, Val: S.second); |
635 | for (auto S : SymMap.Map) |
636 | Out << S.first << '\0'; |
637 | |
638 | while (Pad--) |
639 | Out.write(C: uint8_t(0)); |
640 | } |
641 | |
642 | static void writeECSymbols(raw_ostream &Out, object::Archive::Kind Kind, |
643 | bool Deterministic, ArrayRef<MemberData> Members, |
644 | SymMap &SymMap) { |
645 | uint32_t Pad; |
646 | uint64_t Size = computeECSymbolsSize(SymMap, Padding: &Pad); |
647 | printGNUSmallMemberHeader(Out, Name: "/<ECSYMBOLS>" , ModTime: now(Deterministic), UID: 0, GID: 0, Perms: 0, |
648 | Size); |
649 | |
650 | printLE<uint32_t>(Out, Val: SymMap.ECMap.size()); |
651 | |
652 | for (auto S : SymMap.ECMap) |
653 | printLE(Out, Val: S.second); |
654 | for (auto S : SymMap.ECMap) |
655 | Out << S.first << '\0'; |
656 | while (Pad--) |
657 | Out.write(C: uint8_t(0)); |
658 | } |
659 | |
660 | static bool isECObject(object::SymbolicFile &Obj) { |
661 | if (Obj.isCOFF()) |
662 | return cast<llvm::object::COFFObjectFile>(Val: &Obj)->getMachine() != |
663 | COFF::IMAGE_FILE_MACHINE_ARM64; |
664 | |
665 | if (Obj.isCOFFImportFile()) |
666 | return cast<llvm::object::COFFImportFile>(Val: &Obj)->getMachine() != |
667 | COFF::IMAGE_FILE_MACHINE_ARM64; |
668 | |
669 | if (Obj.isIR()) { |
670 | Expected<std::string> TripleStr = |
671 | getBitcodeTargetTriple(Buffer: Obj.getMemoryBufferRef()); |
672 | if (!TripleStr) |
673 | return false; |
674 | Triple T(*TripleStr); |
675 | return T.isWindowsArm64EC() || T.getArch() == Triple::x86_64; |
676 | } |
677 | |
678 | return false; |
679 | } |
680 | |
681 | static bool isAnyArm64COFF(object::SymbolicFile &Obj) { |
682 | if (Obj.isCOFF()) |
683 | return COFF::isAnyArm64(Machine: cast<COFFObjectFile>(Val: &Obj)->getMachine()); |
684 | |
685 | if (Obj.isCOFFImportFile()) |
686 | return COFF::isAnyArm64(Machine: cast<COFFImportFile>(Val: &Obj)->getMachine()); |
687 | |
688 | if (Obj.isIR()) { |
689 | Expected<std::string> TripleStr = |
690 | getBitcodeTargetTriple(Buffer: Obj.getMemoryBufferRef()); |
691 | if (!TripleStr) |
692 | return false; |
693 | Triple T(*TripleStr); |
694 | return T.isOSWindows() && T.getArch() == Triple::aarch64; |
695 | } |
696 | |
697 | return false; |
698 | } |
699 | |
700 | bool isImportDescriptor(StringRef Name) { |
701 | return Name.starts_with(Prefix: ImportDescriptorPrefix) || |
702 | Name == StringRef{NullImportDescriptorSymbolName} || |
703 | (Name.starts_with(Prefix: NullThunkDataPrefix) && |
704 | Name.ends_with(Suffix: NullThunkDataSuffix)); |
705 | } |
706 | |
707 | static Expected<std::vector<unsigned>> getSymbols(SymbolicFile *Obj, |
708 | uint16_t Index, |
709 | raw_ostream &SymNames, |
710 | SymMap *SymMap) { |
711 | std::vector<unsigned> Ret; |
712 | |
713 | if (Obj == nullptr) |
714 | return Ret; |
715 | |
716 | std::map<std::string, uint16_t> *Map = nullptr; |
717 | if (SymMap) |
718 | Map = SymMap->UseECMap && isECObject(Obj&: *Obj) ? &SymMap->ECMap : &SymMap->Map; |
719 | |
720 | for (const object::BasicSymbolRef &S : Obj->symbols()) { |
721 | if (!isArchiveSymbol(S)) |
722 | continue; |
723 | if (Map) { |
724 | std::string Name; |
725 | raw_string_ostream NameStream(Name); |
726 | if (Error E = S.printName(OS&: NameStream)) |
727 | return std::move(E); |
728 | if (Map->find(x: Name) != Map->end()) |
729 | continue; // ignore duplicated symbol |
730 | (*Map)[Name] = Index; |
731 | if (Map == &SymMap->Map) { |
732 | Ret.push_back(x: SymNames.tell()); |
733 | SymNames << Name << '\0'; |
734 | // If EC is enabled, then the import descriptors are NOT put into EC |
735 | // objects so we need to copy them to the EC map manually. |
736 | if (SymMap->UseECMap && isImportDescriptor(Name)) |
737 | SymMap->ECMap[Name] = Index; |
738 | } |
739 | } else { |
740 | Ret.push_back(x: SymNames.tell()); |
741 | if (Error E = S.printName(OS&: SymNames)) |
742 | return std::move(E); |
743 | SymNames << '\0'; |
744 | } |
745 | } |
746 | return Ret; |
747 | } |
748 | |
749 | static Expected<std::vector<MemberData>> |
750 | computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames, |
751 | object::Archive::Kind Kind, bool Thin, bool Deterministic, |
752 | SymtabWritingMode NeedSymbols, SymMap *SymMap, |
753 | LLVMContext &Context, ArrayRef<NewArchiveMember> NewMembers, |
754 | std::optional<bool> IsEC) { |
755 | static char PaddingData[8] = {'\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n'}; |
756 | uint64_t MemHeadPadSize = 0; |
757 | uint64_t Pos = |
758 | isAIXBigArchive(Kind) ? sizeof(object::BigArchive::FixLenHdr) : 0; |
759 | |
760 | std::vector<MemberData> Ret; |
761 | bool HasObject = false; |
762 | |
763 | // Deduplicate long member names in the string table and reuse earlier name |
764 | // offsets. This especially saves space for COFF Import libraries where all |
765 | // members have the same name. |
766 | StringMap<uint64_t> MemberNames; |
767 | |
768 | // UniqueTimestamps is a special case to improve debugging on Darwin: |
769 | // |
770 | // The Darwin linker does not link debug info into the final |
771 | // binary. Instead, it emits entries of type N_OSO in the output |
772 | // binary's symbol table, containing references to the linked-in |
773 | // object files. Using that reference, the debugger can read the |
774 | // debug data directly from the object files. Alternatively, an |
775 | // invocation of 'dsymutil' will link the debug data from the object |
776 | // files into a dSYM bundle, which can be loaded by the debugger, |
777 | // instead of the object files. |
778 | // |
779 | // For an object file, the N_OSO entries contain the absolute path |
780 | // path to the file, and the file's timestamp. For an object |
781 | // included in an archive, the path is formatted like |
782 | // "/absolute/path/to/archive.a(member.o)", and the timestamp is the |
783 | // archive member's timestamp, rather than the archive's timestamp. |
784 | // |
785 | // However, this doesn't always uniquely identify an object within |
786 | // an archive -- an archive file can have multiple entries with the |
787 | // same filename. (This will happen commonly if the original object |
788 | // files started in different directories.) The only way they get |
789 | // distinguished, then, is via the timestamp. But this process is |
790 | // unable to find the correct object file in the archive when there |
791 | // are two files of the same name and timestamp. |
792 | // |
793 | // Additionally, timestamp==0 is treated specially, and causes the |
794 | // timestamp to be ignored as a match criteria. |
795 | // |
796 | // That will "usually" work out okay when creating an archive not in |
797 | // deterministic timestamp mode, because the objects will probably |
798 | // have been created at different timestamps. |
799 | // |
800 | // To ameliorate this problem, in deterministic archive mode (which |
801 | // is the default), on Darwin we will emit a unique non-zero |
802 | // timestamp for each entry with a duplicated name. This is still |
803 | // deterministic: the only thing affecting that timestamp is the |
804 | // order of the files in the resultant archive. |
805 | // |
806 | // See also the functions that handle the lookup: |
807 | // in lldb: ObjectContainerBSDArchive::Archive::FindObject() |
808 | // in llvm/tools/dsymutil: BinaryHolder::GetArchiveMemberBuffers(). |
809 | bool UniqueTimestamps = Deterministic && isDarwin(Kind); |
810 | std::map<StringRef, unsigned> FilenameCount; |
811 | if (UniqueTimestamps) { |
812 | for (const NewArchiveMember &M : NewMembers) |
813 | FilenameCount[M.MemberName]++; |
814 | for (auto &Entry : FilenameCount) |
815 | Entry.second = Entry.second > 1 ? 1 : 0; |
816 | } |
817 | |
818 | std::vector<std::unique_ptr<SymbolicFile>> SymFiles; |
819 | |
820 | if (NeedSymbols != SymtabWritingMode::NoSymtab || isAIXBigArchive(Kind)) { |
821 | for (const NewArchiveMember &M : NewMembers) { |
822 | Expected<std::unique_ptr<SymbolicFile>> SymFileOrErr = |
823 | getSymbolicFile(Buf: M.Buf->getMemBufferRef(), Context); |
824 | if (!SymFileOrErr) |
825 | return createFileError(F: M.MemberName, E: SymFileOrErr.takeError()); |
826 | SymFiles.push_back(x: std::move(*SymFileOrErr)); |
827 | } |
828 | } |
829 | |
830 | if (SymMap) { |
831 | if (IsEC) { |
832 | SymMap->UseECMap = *IsEC; |
833 | } else { |
834 | // When IsEC is not specified by the caller, use it when we have both |
835 | // any ARM64 object (ARM64 or ARM64EC) and any EC object (ARM64EC or |
836 | // AMD64). This may be a single ARM64EC object, but may also be separate |
837 | // ARM64 and AMD64 objects. |
838 | bool HaveArm64 = false, HaveEC = false; |
839 | for (std::unique_ptr<SymbolicFile> &SymFile : SymFiles) { |
840 | if (!SymFile) |
841 | continue; |
842 | if (!HaveArm64) |
843 | HaveArm64 = isAnyArm64COFF(Obj&: *SymFile); |
844 | if (!HaveEC) |
845 | HaveEC = isECObject(Obj&: *SymFile); |
846 | if (HaveArm64 && HaveEC) { |
847 | SymMap->UseECMap = true; |
848 | break; |
849 | } |
850 | } |
851 | } |
852 | } |
853 | |
854 | // The big archive format needs to know the offset of the previous member |
855 | // header. |
856 | uint64_t PrevOffset = 0; |
857 | uint64_t NextMemHeadPadSize = 0; |
858 | |
859 | for (uint32_t Index = 0; Index < NewMembers.size(); ++Index) { |
860 | const NewArchiveMember *M = &NewMembers[Index]; |
861 | std::string ; |
862 | raw_string_ostream Out(Header); |
863 | |
864 | MemoryBufferRef Buf = M->Buf->getMemBufferRef(); |
865 | StringRef Data = Thin ? "" : Buf.getBuffer(); |
866 | |
867 | // ld64 expects the members to be 8-byte aligned for 64-bit content and at |
868 | // least 4-byte aligned for 32-bit content. Opt for the larger encoding |
869 | // uniformly. This matches the behaviour with cctools and ensures that ld64 |
870 | // is happy with archives that we generate. |
871 | unsigned MemberPadding = |
872 | isDarwin(Kind) ? offsetToAlignment(Value: Data.size(), Alignment: Align(8)) : 0; |
873 | unsigned TailPadding = |
874 | offsetToAlignment(Value: Data.size() + MemberPadding, Alignment: Align(2)); |
875 | StringRef Padding = StringRef(PaddingData, MemberPadding + TailPadding); |
876 | |
877 | sys::TimePoint<std::chrono::seconds> ModTime; |
878 | if (UniqueTimestamps) |
879 | // Increment timestamp for each file of a given name. |
880 | ModTime = sys::toTimePoint(T: FilenameCount[M->MemberName]++); |
881 | else |
882 | ModTime = M->ModTime; |
883 | |
884 | uint64_t Size = Buf.getBufferSize() + MemberPadding; |
885 | if (Size > object::Archive::MaxMemberSize) { |
886 | std::string StringMsg = |
887 | "File " + M->MemberName.str() + " exceeds size limit" ; |
888 | return make_error<object::GenericBinaryError>( |
889 | Args: std::move(StringMsg), Args: object::object_error::parse_failed); |
890 | } |
891 | |
892 | std::unique_ptr<SymbolicFile> CurSymFile; |
893 | if (!SymFiles.empty()) |
894 | CurSymFile = std::move(SymFiles[Index]); |
895 | |
896 | // In the big archive file format, we need to calculate and include the next |
897 | // member offset and previous member offset in the file member header. |
898 | if (isAIXBigArchive(Kind)) { |
899 | uint64_t OffsetToMemData = Pos + sizeof(object::BigArMemHdrType) + |
900 | alignTo(Value: M->MemberName.size(), Align: 2); |
901 | |
902 | if (M == NewMembers.begin()) |
903 | NextMemHeadPadSize = |
904 | alignToPowerOf2(Value: OffsetToMemData, |
905 | Align: getMemberAlignment(SymObj: CurSymFile.get())) - |
906 | OffsetToMemData; |
907 | |
908 | MemHeadPadSize = NextMemHeadPadSize; |
909 | Pos += MemHeadPadSize; |
910 | uint64_t NextOffset = Pos + sizeof(object::BigArMemHdrType) + |
911 | alignTo(Value: M->MemberName.size(), Align: 2) + alignTo(Value: Size, Align: 2); |
912 | |
913 | // If there is another member file after this, we need to calculate the |
914 | // padding before the header. |
915 | if (Index + 1 != SymFiles.size()) { |
916 | uint64_t OffsetToNextMemData = |
917 | NextOffset + sizeof(object::BigArMemHdrType) + |
918 | alignTo(Value: NewMembers[Index + 1].MemberName.size(), Align: 2); |
919 | NextMemHeadPadSize = |
920 | alignToPowerOf2(Value: OffsetToNextMemData, |
921 | Align: getMemberAlignment(SymObj: SymFiles[Index + 1].get())) - |
922 | OffsetToNextMemData; |
923 | NextOffset += NextMemHeadPadSize; |
924 | } |
925 | printBigArchiveMemberHeader(Out, Name: M->MemberName, ModTime, UID: M->UID, GID: M->GID, |
926 | Perms: M->Perms, Size, PrevOffset, NextOffset); |
927 | PrevOffset = Pos; |
928 | } else { |
929 | printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M: *M, |
930 | ModTime, Size); |
931 | } |
932 | Out.flush(); |
933 | |
934 | std::vector<unsigned> Symbols; |
935 | if (NeedSymbols != SymtabWritingMode::NoSymtab) { |
936 | Expected<std::vector<unsigned>> SymbolsOrErr = |
937 | getSymbols(Obj: CurSymFile.get(), Index: Index + 1, SymNames, SymMap); |
938 | if (!SymbolsOrErr) |
939 | return createFileError(F: M->MemberName, E: SymbolsOrErr.takeError()); |
940 | Symbols = std::move(*SymbolsOrErr); |
941 | if (CurSymFile) |
942 | HasObject = true; |
943 | } |
944 | |
945 | Pos += Header.size() + Data.size() + Padding.size(); |
946 | Ret.push_back(x: {.Symbols: std::move(Symbols), .Header: std::move(Header), .Data: Data, .Padding: Padding, |
947 | .PreHeadPadSize: MemHeadPadSize, .SymFile: std::move(CurSymFile)}); |
948 | } |
949 | // If there are no symbols, emit an empty symbol table, to satisfy Solaris |
950 | // tools, older versions of which expect a symbol table in a non-empty |
951 | // archive, regardless of whether there are any symbols in it. |
952 | if (HasObject && SymNames.tell() == 0 && !isCOFFArchive(Kind)) |
953 | SymNames << '\0' << '\0' << '\0'; |
954 | return std::move(Ret); |
955 | } |
956 | |
957 | namespace llvm { |
958 | |
959 | static ErrorOr<SmallString<128>> canonicalizePath(StringRef P) { |
960 | SmallString<128> Ret = P; |
961 | std::error_code Err = sys::fs::make_absolute(path&: Ret); |
962 | if (Err) |
963 | return Err; |
964 | sys::path::remove_dots(path&: Ret, /*removedotdot*/ remove_dot_dot: true); |
965 | return Ret; |
966 | } |
967 | |
968 | // Compute the relative path from From to To. |
969 | Expected<std::string> computeArchiveRelativePath(StringRef From, StringRef To) { |
970 | ErrorOr<SmallString<128>> PathToOrErr = canonicalizePath(P: To); |
971 | ErrorOr<SmallString<128>> DirFromOrErr = canonicalizePath(P: From); |
972 | if (!PathToOrErr || !DirFromOrErr) |
973 | return errorCodeToError(EC: errnoAsErrorCode()); |
974 | |
975 | const SmallString<128> &PathTo = *PathToOrErr; |
976 | const SmallString<128> &DirFrom = sys::path::parent_path(path: *DirFromOrErr); |
977 | |
978 | // Can't construct a relative path between different roots |
979 | if (sys::path::root_name(path: PathTo) != sys::path::root_name(path: DirFrom)) |
980 | return sys::path::convert_to_slash(path: PathTo); |
981 | |
982 | // Skip common prefixes |
983 | auto FromTo = |
984 | std::mismatch(first1: sys::path::begin(path: DirFrom), last1: sys::path::end(path: DirFrom), |
985 | first2: sys::path::begin(path: PathTo)); |
986 | auto FromI = FromTo.first; |
987 | auto ToI = FromTo.second; |
988 | |
989 | // Construct relative path |
990 | SmallString<128> Relative; |
991 | for (auto FromE = sys::path::end(path: DirFrom); FromI != FromE; ++FromI) |
992 | sys::path::append(path&: Relative, style: sys::path::Style::posix, a: ".." ); |
993 | |
994 | for (auto ToE = sys::path::end(path: PathTo); ToI != ToE; ++ToI) |
995 | sys::path::append(path&: Relative, style: sys::path::Style::posix, a: *ToI); |
996 | |
997 | return std::string(Relative); |
998 | } |
999 | |
1000 | static Error |
1001 | writeArchiveToStream(raw_ostream &Out, ArrayRef<NewArchiveMember> NewMembers, |
1002 | SymtabWritingMode WriteSymtab, object::Archive::Kind Kind, |
1003 | bool Deterministic, bool Thin, std::optional<bool> IsEC) { |
1004 | assert((!Thin || !isBSDLike(Kind)) && "Only the gnu format has a thin mode" ); |
1005 | |
1006 | SmallString<0> SymNamesBuf; |
1007 | raw_svector_ostream SymNames(SymNamesBuf); |
1008 | SmallString<0> StringTableBuf; |
1009 | raw_svector_ostream StringTable(StringTableBuf); |
1010 | SymMap SymMap; |
1011 | bool ShouldWriteSymtab = WriteSymtab != SymtabWritingMode::NoSymtab; |
1012 | |
1013 | // COFF symbol map uses 16-bit indexes, so we can't use it if there are too |
1014 | // many members. COFF format also requires symbol table presence, so use |
1015 | // GNU format when NoSymtab is requested. |
1016 | if (isCOFFArchive(Kind) && (NewMembers.size() > 0xfffe || !ShouldWriteSymtab)) |
1017 | Kind = object::Archive::K_GNU; |
1018 | |
1019 | // In the scenario when LLVMContext is populated SymbolicFile will contain a |
1020 | // reference to it, thus SymbolicFile should be destroyed first. |
1021 | LLVMContext Context; |
1022 | |
1023 | Expected<std::vector<MemberData>> DataOrErr = computeMemberData( |
1024 | StringTable, SymNames, Kind, Thin, Deterministic, NeedSymbols: WriteSymtab, |
1025 | SymMap: isCOFFArchive(Kind) ? &SymMap : nullptr, Context, NewMembers, IsEC); |
1026 | if (Error E = DataOrErr.takeError()) |
1027 | return E; |
1028 | std::vector<MemberData> &Data = *DataOrErr; |
1029 | |
1030 | uint64_t StringTableSize = 0; |
1031 | MemberData StringTableMember; |
1032 | if (!StringTableBuf.empty() && !isAIXBigArchive(Kind)) { |
1033 | StringTableMember = computeStringTable(Names: StringTableBuf); |
1034 | StringTableSize = StringTableMember.Header.size() + |
1035 | StringTableMember.Data.size() + |
1036 | StringTableMember.Padding.size(); |
1037 | } |
1038 | |
1039 | // We would like to detect if we need to switch to a 64-bit symbol table. |
1040 | uint64_t LastMemberEndOffset = 0; |
1041 | uint64_t = 0; |
1042 | uint64_t NumSyms = 0; |
1043 | uint64_t NumSyms32 = 0; // Store symbol number of 32-bit member files. |
1044 | |
1045 | for (const auto &M : Data) { |
1046 | // Record the start of the member's offset |
1047 | LastMemberEndOffset += M.PreHeadPadSize; |
1048 | LastMemberHeaderOffset = LastMemberEndOffset; |
1049 | // Account for the size of each part associated with the member. |
1050 | LastMemberEndOffset += M.Header.size() + M.Data.size() + M.Padding.size(); |
1051 | NumSyms += M.Symbols.size(); |
1052 | |
1053 | // AIX big archive files may contain two global symbol tables. The |
1054 | // first global symbol table locates 32-bit file members that define global |
1055 | // symbols; the second global symbol table does the same for 64-bit file |
1056 | // members. As a big archive can have both 32-bit and 64-bit file members, |
1057 | // we need to know the number of symbols in each symbol table individually. |
1058 | if (isAIXBigArchive(Kind) && ShouldWriteSymtab) { |
1059 | if (!is64BitSymbolicFile(SymObj: M.SymFile.get())) |
1060 | NumSyms32 += M.Symbols.size(); |
1061 | } |
1062 | } |
1063 | |
1064 | std::optional<uint64_t> ; |
1065 | |
1066 | // The symbol table is put at the end of the big archive file. The symbol |
1067 | // table is at the start of the archive file for other archive formats. |
1068 | if (ShouldWriteSymtab && !is64BitKind(Kind)) { |
1069 | // We assume 32-bit offsets to see if 32-bit symbols are possible or not. |
1070 | HeadersSize = computeHeadersSize(Kind, NumMembers: Data.size(), StringMemberSize: StringTableSize, |
1071 | NumSyms, SymNamesSize: SymNamesBuf.size(), |
1072 | SymMap: isCOFFArchive(Kind) ? &SymMap : nullptr); |
1073 | |
1074 | // The SYM64 format is used when an archive's member offsets are larger than |
1075 | // 32-bits can hold. The need for this shift in format is detected by |
1076 | // writeArchive. To test this we need to generate a file with a member that |
1077 | // has an offset larger than 32-bits but this demands a very slow test. To |
1078 | // speed the test up we use this environment variable to pretend like the |
1079 | // cutoff happens before 32-bits and instead happens at some much smaller |
1080 | // value. |
1081 | uint64_t Sym64Threshold = 1ULL << 32; |
1082 | const char *Sym64Env = std::getenv(name: "SYM64_THRESHOLD" ); |
1083 | if (Sym64Env) |
1084 | StringRef(Sym64Env).getAsInteger(Radix: 10, Result&: Sym64Threshold); |
1085 | |
1086 | // If LastMemberHeaderOffset isn't going to fit in a 32-bit varible we need |
1087 | // to switch to 64-bit. Note that the file can be larger than 4GB as long as |
1088 | // the last member starts before the 4GB offset. |
1089 | if (*HeadersSize + LastMemberHeaderOffset >= Sym64Threshold) { |
1090 | if (Kind == object::Archive::K_DARWIN) |
1091 | Kind = object::Archive::K_DARWIN64; |
1092 | else |
1093 | Kind = object::Archive::K_GNU64; |
1094 | HeadersSize.reset(); |
1095 | } |
1096 | } |
1097 | |
1098 | if (Thin) |
1099 | Out << "!<thin>\n" ; |
1100 | else if (isAIXBigArchive(Kind)) |
1101 | Out << "<bigaf>\n" ; |
1102 | else |
1103 | Out << "!<arch>\n" ; |
1104 | |
1105 | if (!isAIXBigArchive(Kind)) { |
1106 | if (ShouldWriteSymtab) { |
1107 | if (!HeadersSize) |
1108 | HeadersSize = computeHeadersSize( |
1109 | Kind, NumMembers: Data.size(), StringMemberSize: StringTableSize, NumSyms, SymNamesSize: SymNamesBuf.size(), |
1110 | SymMap: isCOFFArchive(Kind) ? &SymMap : nullptr); |
1111 | writeSymbolTable(Out, Kind, Deterministic, Members: Data, StringTable: SymNamesBuf, |
1112 | MembersOffset: *HeadersSize, NumSyms); |
1113 | |
1114 | if (isCOFFArchive(Kind)) |
1115 | writeSymbolMap(Out, Kind, Deterministic, Members: Data, SymMap, MembersOffset: *HeadersSize); |
1116 | } |
1117 | |
1118 | if (StringTableSize) |
1119 | Out << StringTableMember.Header << StringTableMember.Data |
1120 | << StringTableMember.Padding; |
1121 | |
1122 | if (ShouldWriteSymtab && SymMap.ECMap.size()) |
1123 | writeECSymbols(Out, Kind, Deterministic, Members: Data, SymMap); |
1124 | |
1125 | for (const MemberData &M : Data) |
1126 | Out << M.Header << M.Data << M.Padding; |
1127 | } else { |
1128 | HeadersSize = sizeof(object::BigArchive::FixLenHdr); |
1129 | LastMemberEndOffset += *HeadersSize; |
1130 | LastMemberHeaderOffset += *HeadersSize; |
1131 | |
1132 | // For the big archive (AIX) format, compute a table of member names and |
1133 | // offsets, used in the member table. |
1134 | uint64_t MemberTableNameStrTblSize = 0; |
1135 | std::vector<size_t> MemberOffsets; |
1136 | std::vector<StringRef> MemberNames; |
1137 | // Loop across object to find offset and names. |
1138 | uint64_t MemberEndOffset = sizeof(object::BigArchive::FixLenHdr); |
1139 | for (size_t I = 0, Size = NewMembers.size(); I != Size; ++I) { |
1140 | const NewArchiveMember &Member = NewMembers[I]; |
1141 | MemberTableNameStrTblSize += Member.MemberName.size() + 1; |
1142 | MemberEndOffset += Data[I].PreHeadPadSize; |
1143 | MemberOffsets.push_back(x: MemberEndOffset); |
1144 | MemberNames.push_back(x: Member.MemberName); |
1145 | // File member name ended with "`\n". The length is included in |
1146 | // BigArMemHdrType. |
1147 | MemberEndOffset += sizeof(object::BigArMemHdrType) + |
1148 | alignTo(Value: Data[I].Data.size(), Align: 2) + |
1149 | alignTo(Value: Member.MemberName.size(), Align: 2); |
1150 | } |
1151 | |
1152 | // AIX member table size. |
1153 | uint64_t MemberTableSize = 20 + // Number of members field |
1154 | 20 * MemberOffsets.size() + |
1155 | MemberTableNameStrTblSize; |
1156 | |
1157 | SmallString<0> SymNamesBuf32; |
1158 | SmallString<0> SymNamesBuf64; |
1159 | raw_svector_ostream SymNames32(SymNamesBuf32); |
1160 | raw_svector_ostream SymNames64(SymNamesBuf64); |
1161 | |
1162 | if (ShouldWriteSymtab && NumSyms) |
1163 | // Generate the symbol names for the members. |
1164 | for (const auto &M : Data) { |
1165 | Expected<std::vector<unsigned>> SymbolsOrErr = getSymbols( |
1166 | Obj: M.SymFile.get(), Index: 0, |
1167 | SymNames&: is64BitSymbolicFile(SymObj: M.SymFile.get()) ? SymNames64 : SymNames32, |
1168 | SymMap: nullptr); |
1169 | if (!SymbolsOrErr) |
1170 | return SymbolsOrErr.takeError(); |
1171 | } |
1172 | |
1173 | uint64_t MemberTableEndOffset = |
1174 | LastMemberEndOffset + |
1175 | alignTo(Value: sizeof(object::BigArMemHdrType) + MemberTableSize, Align: 2); |
1176 | |
1177 | // In AIX OS, The 'GlobSymOffset' field in the fixed-length header contains |
1178 | // the offset to the 32-bit global symbol table, and the 'GlobSym64Offset' |
1179 | // contains the offset to the 64-bit global symbol table. |
1180 | uint64_t GlobalSymbolOffset = |
1181 | (ShouldWriteSymtab && |
1182 | (WriteSymtab != SymtabWritingMode::BigArchive64) && NumSyms32 > 0) |
1183 | ? MemberTableEndOffset |
1184 | : 0; |
1185 | |
1186 | uint64_t GlobalSymbolOffset64 = 0; |
1187 | uint64_t NumSyms64 = NumSyms - NumSyms32; |
1188 | if (ShouldWriteSymtab && (WriteSymtab != SymtabWritingMode::BigArchive32) && |
1189 | NumSyms64 > 0) { |
1190 | if (GlobalSymbolOffset == 0) |
1191 | GlobalSymbolOffset64 = MemberTableEndOffset; |
1192 | else |
1193 | // If there is a global symbol table for 32-bit members, |
1194 | // the 64-bit global symbol table is after the 32-bit one. |
1195 | GlobalSymbolOffset64 = |
1196 | GlobalSymbolOffset + sizeof(object::BigArMemHdrType) + |
1197 | (NumSyms32 + 1) * 8 + alignTo(Value: SymNamesBuf32.size(), Align: 2); |
1198 | } |
1199 | |
1200 | // Fixed Sized Header. |
1201 | printWithSpacePadding(OS&: Out, Data: NewMembers.size() ? LastMemberEndOffset : 0, |
1202 | Size: 20); // Offset to member table |
1203 | // If there are no file members in the archive, there will be no global |
1204 | // symbol table. |
1205 | printWithSpacePadding(OS&: Out, Data: GlobalSymbolOffset, Size: 20); |
1206 | printWithSpacePadding(OS&: Out, Data: GlobalSymbolOffset64, Size: 20); |
1207 | printWithSpacePadding(OS&: Out, |
1208 | Data: NewMembers.size() |
1209 | ? sizeof(object::BigArchive::FixLenHdr) + |
1210 | Data[0].PreHeadPadSize |
1211 | : 0, |
1212 | Size: 20); // Offset to first archive member |
1213 | printWithSpacePadding(OS&: Out, Data: NewMembers.size() ? LastMemberHeaderOffset : 0, |
1214 | Size: 20); // Offset to last archive member |
1215 | printWithSpacePadding( |
1216 | OS&: Out, Data: 0, |
1217 | Size: 20); // Offset to first member of free list - Not supported yet |
1218 | |
1219 | for (const MemberData &M : Data) { |
1220 | Out << std::string(M.PreHeadPadSize, '\0'); |
1221 | Out << M.Header << M.Data; |
1222 | if (M.Data.size() % 2) |
1223 | Out << '\0'; |
1224 | } |
1225 | |
1226 | if (NewMembers.size()) { |
1227 | // Member table. |
1228 | printBigArchiveMemberHeader(Out, Name: "" , ModTime: sys::toTimePoint(T: 0), UID: 0, GID: 0, Perms: 0, |
1229 | Size: MemberTableSize, PrevOffset: LastMemberHeaderOffset, |
1230 | NextOffset: GlobalSymbolOffset ? GlobalSymbolOffset |
1231 | : GlobalSymbolOffset64); |
1232 | printWithSpacePadding(OS&: Out, Data: MemberOffsets.size(), Size: 20); // Number of members |
1233 | for (uint64_t MemberOffset : MemberOffsets) |
1234 | printWithSpacePadding(OS&: Out, Data: MemberOffset, |
1235 | Size: 20); // Offset to member file header. |
1236 | for (StringRef MemberName : MemberNames) |
1237 | Out << MemberName << '\0'; // Member file name, null byte padding. |
1238 | |
1239 | if (MemberTableNameStrTblSize % 2) |
1240 | Out << '\0'; // Name table must be tail padded to an even number of |
1241 | // bytes. |
1242 | |
1243 | if (ShouldWriteSymtab) { |
1244 | // Write global symbol table for 32-bit file members. |
1245 | if (GlobalSymbolOffset) { |
1246 | writeSymbolTable(Out, Kind, Deterministic, Members: Data, StringTable: SymNamesBuf32, |
1247 | MembersOffset: *HeadersSize, NumSyms: NumSyms32, PrevMemberOffset: LastMemberEndOffset, |
1248 | NextMemberOffset: GlobalSymbolOffset64); |
1249 | // Add padding between the symbol tables, if needed. |
1250 | if (GlobalSymbolOffset64 && (SymNamesBuf32.size() % 2)) |
1251 | Out << '\0'; |
1252 | } |
1253 | |
1254 | // Write global symbol table for 64-bit file members. |
1255 | if (GlobalSymbolOffset64) |
1256 | writeSymbolTable(Out, Kind, Deterministic, Members: Data, StringTable: SymNamesBuf64, |
1257 | MembersOffset: *HeadersSize, NumSyms: NumSyms64, |
1258 | PrevMemberOffset: GlobalSymbolOffset ? GlobalSymbolOffset |
1259 | : LastMemberEndOffset, |
1260 | NextMemberOffset: 0, Is64Bit: true); |
1261 | } |
1262 | } |
1263 | } |
1264 | Out.flush(); |
1265 | return Error::success(); |
1266 | } |
1267 | |
1268 | Error writeArchive(StringRef ArcName, ArrayRef<NewArchiveMember> NewMembers, |
1269 | SymtabWritingMode WriteSymtab, object::Archive::Kind Kind, |
1270 | bool Deterministic, bool Thin, |
1271 | std::unique_ptr<MemoryBuffer> OldArchiveBuf, |
1272 | std::optional<bool> IsEC) { |
1273 | Expected<sys::fs::TempFile> Temp = |
1274 | sys::fs::TempFile::create(Model: ArcName + ".temp-archive-%%%%%%%.a" ); |
1275 | if (!Temp) |
1276 | return Temp.takeError(); |
1277 | raw_fd_ostream Out(Temp->FD, false); |
1278 | |
1279 | if (Error E = writeArchiveToStream(Out, NewMembers, WriteSymtab, Kind, |
1280 | Deterministic, Thin, IsEC)) { |
1281 | if (Error DiscardError = Temp->discard()) |
1282 | return joinErrors(E1: std::move(E), E2: std::move(DiscardError)); |
1283 | return E; |
1284 | } |
1285 | |
1286 | // At this point, we no longer need whatever backing memory |
1287 | // was used to generate the NewMembers. On Windows, this buffer |
1288 | // could be a mapped view of the file we want to replace (if |
1289 | // we're updating an existing archive, say). In that case, the |
1290 | // rename would still succeed, but it would leave behind a |
1291 | // temporary file (actually the original file renamed) because |
1292 | // a file cannot be deleted while there's a handle open on it, |
1293 | // only renamed. So by freeing this buffer, this ensures that |
1294 | // the last open handle on the destination file, if any, is |
1295 | // closed before we attempt to rename. |
1296 | OldArchiveBuf.reset(); |
1297 | |
1298 | return Temp->keep(Name: ArcName); |
1299 | } |
1300 | |
1301 | Expected<std::unique_ptr<MemoryBuffer>> |
1302 | writeArchiveToBuffer(ArrayRef<NewArchiveMember> NewMembers, |
1303 | SymtabWritingMode WriteSymtab, object::Archive::Kind Kind, |
1304 | bool Deterministic, bool Thin) { |
1305 | SmallVector<char, 0> ArchiveBufferVector; |
1306 | raw_svector_ostream ArchiveStream(ArchiveBufferVector); |
1307 | |
1308 | if (Error E = writeArchiveToStream(Out&: ArchiveStream, NewMembers, WriteSymtab, |
1309 | Kind, Deterministic, Thin, IsEC: std::nullopt)) |
1310 | return std::move(E); |
1311 | |
1312 | return std::make_unique<SmallVectorMemoryBuffer>( |
1313 | args: std::move(ArchiveBufferVector), /*RequiresNullTerminator=*/args: false); |
1314 | } |
1315 | |
1316 | } // namespace llvm |
1317 | |