1 //===-- llvm-libtool-darwin.cpp - a tool for creating libraries -----------===//
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 // A utility for creating static and dynamic libraries for Darwin.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/BinaryFormat/Magic.h"
14 #include "llvm/IR/LLVMContext.h"
15 #include "llvm/Object/ArchiveWriter.h"
16 #include "llvm/Object/IRObjectFile.h"
17 #include "llvm/Object/MachO.h"
18 #include "llvm/Object/MachOUniversal.h"
19 #include "llvm/Object/MachOUniversalWriter.h"
20 #include "llvm/Object/ObjectFile.h"
21 #include "llvm/Support/CommandLine.h"
22 #include "llvm/Support/InitLLVM.h"
23 #include "llvm/Support/LineIterator.h"
24 #include "llvm/Support/WithColor.h"
25 #include "llvm/TextAPI/MachO/Architecture.h"
26 #include <map>
27 
28 using namespace llvm;
29 using namespace llvm::object;
30 
31 static LLVMContext LLVMCtx;
32 
33 typedef std::map<uint64_t, std::vector<NewArchiveMember>>
34     MembersPerArchitectureMap;
35 
36 cl::OptionCategory LibtoolCategory("llvm-libtool-darwin Options");
37 
38 static cl::opt<std::string> OutputFile("o", cl::desc("Specify output filename"),
39                                        cl::value_desc("filename"),
40                                        cl::cat(LibtoolCategory));
41 
42 static cl::list<std::string> InputFiles(cl::Positional,
43                                         cl::desc("<input files>"),
44                                         cl::ZeroOrMore,
45                                         cl::cat(LibtoolCategory));
46 
47 static cl::opt<std::string> ArchType(
48     "arch_only", cl::desc("Specify architecture type for output library"),
49     cl::value_desc("arch_type"), cl::ZeroOrMore, cl::cat(LibtoolCategory));
50 
51 enum class Operation { None, Static };
52 
53 static cl::opt<Operation> LibraryOperation(
54     cl::desc("Library Type: "),
55     cl::values(
56         clEnumValN(Operation::Static, "static",
57                    "Produce a statically linked library from the input files")),
58     cl::init(Operation::None), cl::cat(LibtoolCategory));
59 
60 static cl::opt<bool> DeterministicOption(
61     "D", cl::desc("Use zero for timestamps and UIDs/GIDs (Default)"),
62     cl::init(false), cl::cat(LibtoolCategory));
63 
64 static cl::opt<bool>
65     NonDeterministicOption("U", cl::desc("Use actual timestamps and UIDs/GIDs"),
66                            cl::init(false), cl::cat(LibtoolCategory));
67 
68 static cl::opt<std::string>
69     FileList("filelist",
70              cl::desc("Pass in file containing a list of filenames"),
71              cl::value_desc("listfile[,dirname]"), cl::cat(LibtoolCategory));
72 
73 static cl::list<std::string> Libraries(
74     "l",
75     cl::desc(
76         "l<x> searches for the library libx.a in the library search path. If"
77         " the string 'x' ends with '.o', then the library 'x' is searched for"
78         " without prepending 'lib' or appending '.a'"),
79     cl::ZeroOrMore, cl::Prefix, cl::cat(LibtoolCategory));
80 
81 static cl::list<std::string> LibrarySearchDirs(
82     "L",
83     cl::desc(
84         "L<dir> adds <dir> to the list of directories in which to search for"
85         " libraries"),
86     cl::ZeroOrMore, cl::Prefix, cl::cat(LibtoolCategory));
87 
88 static cl::opt<bool>
89     VersionOption("V", cl::desc("Print the version number and exit"),
90                   cl::cat(LibtoolCategory));
91 
92 static cl::opt<bool> NoWarningForNoSymbols(
93     "no_warning_for_no_symbols",
94     cl::desc("Do not warn about files that have no symbols"),
95     cl::cat(LibtoolCategory), cl::init(false));
96 
97 static const std::array<std::string, 3> StandardSearchDirs{
98     "/lib",
99     "/usr/lib",
100     "/usr/local/lib",
101 };
102 
103 struct Config {
104   bool Deterministic = true; // Updated by 'D' and 'U' modifiers.
105   uint32_t ArchCPUType;
106   uint32_t ArchCPUSubtype;
107 };
108 
109 static Expected<std::string> searchForFile(const Twine &FileName) {
110 
111   auto FindLib =
112       [FileName](ArrayRef<std::string> SearchDirs) -> Optional<std::string> {
113     for (StringRef Dir : SearchDirs) {
114       SmallString<128> Path;
115       sys::path::append(Path, Dir, FileName);
116 
117       if (sys::fs::exists(Path))
118         return std::string(Path);
119     }
120     return None;
121   };
122 
123   Optional<std::string> Found = FindLib(LibrarySearchDirs);
124   if (!Found)
125     Found = FindLib(StandardSearchDirs);
126   if (Found)
127     return *Found;
128 
129   return createStringError(std::errc::invalid_argument,
130                            "cannot locate file '%s'", FileName.str().c_str());
131 }
132 
133 static Error processCommandLineLibraries() {
134   for (StringRef BaseName : Libraries) {
135     Expected<std::string> FullPath = searchForFile(
136         BaseName.endswith(".o") ? BaseName.str() : "lib" + BaseName + ".a");
137     if (!FullPath)
138       return FullPath.takeError();
139     InputFiles.push_back(FullPath.get());
140   }
141 
142   return Error::success();
143 }
144 
145 static Error processFileList() {
146   StringRef FileName, DirName;
147   std::tie(FileName, DirName) = StringRef(FileList).rsplit(",");
148 
149   ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr =
150       MemoryBuffer::getFileOrSTDIN(FileName, /*FileSize=*/-1,
151                                    /*RequiresNullTerminator=*/false);
152   if (std::error_code EC = FileOrErr.getError())
153     return createFileError(FileName, errorCodeToError(EC));
154   const MemoryBuffer &Ref = *FileOrErr.get();
155 
156   line_iterator I(Ref, /*SkipBlanks=*/false);
157   if (I.is_at_eof())
158     return createStringError(std::errc::invalid_argument,
159                              "file list file: '%s' is empty",
160                              FileName.str().c_str());
161   for (; !I.is_at_eof(); ++I) {
162     StringRef Line = *I;
163     if (Line.empty())
164       return createStringError(std::errc::invalid_argument,
165                                "file list file: '%s': filename cannot be empty",
166                                FileName.str().c_str());
167 
168     SmallString<128> Path;
169     if (!DirName.empty())
170       sys::path::append(Path, DirName, Line);
171     else
172       sys::path::append(Path, Line);
173     InputFiles.push_back(static_cast<std::string>(Path));
174   }
175   return Error::success();
176 }
177 
178 static Error validateArchitectureName(StringRef ArchitectureName) {
179   if (!MachOObjectFile::isValidArch(ArchitectureName)) {
180     std::string Buf;
181     raw_string_ostream OS(Buf);
182     for (StringRef Arch : MachOObjectFile::getValidArchs())
183       OS << Arch << " ";
184 
185     return createStringError(
186         std::errc::invalid_argument,
187         "invalid architecture '%s': valid architecture names are %s",
188         ArchitectureName.str().c_str(), OS.str().c_str());
189   }
190   return Error::success();
191 }
192 
193 static uint64_t getCPUID(uint32_t CPUType, uint32_t CPUSubtype) {
194   switch (CPUType) {
195   case MachO::CPU_TYPE_ARM:
196   case MachO::CPU_TYPE_ARM64:
197   case MachO::CPU_TYPE_ARM64_32:
198   case MachO::CPU_TYPE_X86_64:
199     // We consider CPUSubtype only for the above 4 CPUTypes to match cctools'
200     // libtool behavior.
201     return static_cast<uint64_t>(CPUType) << 32 | CPUSubtype;
202   default:
203     return CPUType;
204   }
205 }
206 
207 // Check that a file's architecture [FileCPUType, FileCPUSubtype]
208 // matches the architecture specified under -arch_only flag.
209 static bool acceptFileArch(uint32_t FileCPUType, uint32_t FileCPUSubtype,
210                            const Config &C) {
211   if (C.ArchCPUType != FileCPUType)
212     return false;
213 
214   switch (C.ArchCPUType) {
215   case MachO::CPU_TYPE_ARM:
216   case MachO::CPU_TYPE_ARM64_32:
217   case MachO::CPU_TYPE_X86_64:
218     return C.ArchCPUSubtype == FileCPUSubtype;
219 
220   case MachO::CPU_TYPE_ARM64:
221     if (C.ArchCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL)
222       return FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_ALL ||
223              FileCPUSubtype == MachO::CPU_SUBTYPE_ARM64_V8;
224     else
225       return C.ArchCPUSubtype == FileCPUSubtype;
226 
227   default:
228     return true;
229   }
230 }
231 
232 static Error verifyAndAddMachOObject(MembersPerArchitectureMap &Members,
233                                      NewArchiveMember Member, const Config &C) {
234   auto MBRef = Member.Buf->getMemBufferRef();
235   Expected<std::unique_ptr<object::ObjectFile>> ObjOrErr =
236       object::ObjectFile::createObjectFile(MBRef);
237 
238   // Throw error if not a valid object file.
239   if (!ObjOrErr)
240     return createFileError(Member.MemberName, ObjOrErr.takeError());
241 
242   // Throw error if not in Mach-O format.
243   if (!isa<object::MachOObjectFile>(**ObjOrErr))
244     return createStringError(std::errc::invalid_argument,
245                              "'%s': format not supported",
246                              Member.MemberName.data());
247 
248   auto *O = dyn_cast<MachOObjectFile>(ObjOrErr->get());
249   uint32_t FileCPUType, FileCPUSubtype;
250   std::tie(FileCPUType, FileCPUSubtype) = MachO::getCPUTypeFromArchitecture(
251       MachO::getArchitectureFromName(O->getArchTriple().getArchName()));
252 
253   // If -arch_only is specified then skip this file if it doesn't match
254   // the architecture specified.
255   if (!ArchType.empty() && !acceptFileArch(FileCPUType, FileCPUSubtype, C)) {
256     return Error::success();
257   }
258 
259   if (!NoWarningForNoSymbols && O->symbols().empty())
260     WithColor::warning() << Member.MemberName + " has no symbols\n";
261 
262   uint64_t FileCPUID = getCPUID(FileCPUType, FileCPUSubtype);
263   Members[FileCPUID].push_back(std::move(Member));
264   return Error::success();
265 }
266 
267 static Error verifyAndAddIRObject(MembersPerArchitectureMap &Members,
268                                   NewArchiveMember Member, const Config &C) {
269   auto MBRef = Member.Buf->getMemBufferRef();
270   Expected<std::unique_ptr<object::IRObjectFile>> IROrErr =
271       object::IRObjectFile::create(MBRef, LLVMCtx);
272 
273   // Throw error if not a valid IR object file.
274   if (!IROrErr)
275     return createFileError(Member.MemberName, IROrErr.takeError());
276 
277   Triple TT = Triple(IROrErr->get()->getTargetTriple());
278 
279   Expected<uint32_t> FileCPUTypeOrErr = MachO::getCPUType(TT);
280   if (!FileCPUTypeOrErr)
281     return FileCPUTypeOrErr.takeError();
282 
283   Expected<uint32_t> FileCPUSubTypeOrErr = MachO::getCPUSubType(TT);
284   if (!FileCPUSubTypeOrErr)
285     return FileCPUSubTypeOrErr.takeError();
286 
287   // If -arch_only is specified then skip this file if it doesn't match
288   // the architecture specified.
289   if (!ArchType.empty() &&
290       !acceptFileArch(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr, C)) {
291     return Error::success();
292   }
293 
294   uint64_t FileCPUID = getCPUID(*FileCPUTypeOrErr, *FileCPUSubTypeOrErr);
295   Members[FileCPUID].push_back(std::move(Member));
296   return Error::success();
297 }
298 
299 static Error addChildMember(MembersPerArchitectureMap &Members,
300                             const object::Archive::Child &M, const Config &C) {
301   Expected<NewArchiveMember> NMOrErr =
302       NewArchiveMember::getOldMember(M, C.Deterministic);
303   if (!NMOrErr)
304     return NMOrErr.takeError();
305 
306   file_magic Magic = identify_magic(NMOrErr->Buf->getBuffer());
307 
308   if (Magic == file_magic::bitcode)
309     return verifyAndAddIRObject(Members, std::move(*NMOrErr), C);
310 
311   if (Error E = verifyAndAddMachOObject(Members, std::move(*NMOrErr), C))
312     return E;
313 
314   return Error::success();
315 }
316 
317 static Error processArchive(MembersPerArchitectureMap &Members,
318                             object::Archive &Lib, StringRef FileName,
319                             const Config &C) {
320   Error Err = Error::success();
321   for (const object::Archive::Child &Child : Lib.children(Err))
322     if (Error E = addChildMember(Members, Child, C))
323       return createFileError(FileName, std::move(E));
324   if (Err)
325     return createFileError(FileName, std::move(Err));
326 
327   return Error::success();
328 }
329 
330 static Error
331 addArchiveMembers(MembersPerArchitectureMap &Members,
332                   std::vector<std::unique_ptr<MemoryBuffer>> &ArchiveBuffers,
333                   NewArchiveMember NM, StringRef FileName, const Config &C) {
334   Expected<std::unique_ptr<Archive>> LibOrErr =
335       object::Archive::create(NM.Buf->getMemBufferRef());
336   if (!LibOrErr)
337     return createFileError(FileName, LibOrErr.takeError());
338 
339   if (Error E = processArchive(Members, **LibOrErr, FileName, C))
340     return E;
341 
342   // Update vector ArchiveBuffers with the MemoryBuffers to transfer
343   // ownership.
344   ArchiveBuffers.push_back(std::move(NM.Buf));
345   return Error::success();
346 }
347 
348 static Error addUniversalMembers(
349     MembersPerArchitectureMap &Members,
350     std::vector<std::unique_ptr<MemoryBuffer>> &UniversalBuffers,
351     NewArchiveMember NM, StringRef FileName, const Config &C) {
352   Expected<std::unique_ptr<MachOUniversalBinary>> BinaryOrErr =
353       MachOUniversalBinary::create(NM.Buf->getMemBufferRef());
354   if (!BinaryOrErr)
355     return createFileError(FileName, BinaryOrErr.takeError());
356 
357   auto *UO = BinaryOrErr->get();
358   for (const MachOUniversalBinary::ObjectForArch &O : UO->objects()) {
359 
360     Expected<std::unique_ptr<MachOObjectFile>> MachOObjOrErr =
361         O.getAsObjectFile();
362     if (MachOObjOrErr) {
363       NewArchiveMember NewMember =
364           NewArchiveMember(MachOObjOrErr->get()->getMemoryBufferRef());
365       NewMember.MemberName = sys::path::filename(NewMember.MemberName);
366 
367       if (Error E = verifyAndAddMachOObject(Members, std::move(NewMember), C))
368         return E;
369       continue;
370     }
371 
372     Expected<std::unique_ptr<IRObjectFile>> IRObjectOrError =
373         O.getAsIRObject(LLVMCtx);
374     if (IRObjectOrError) {
375       // A universal file member can be a MachOObjectFile, an IRObject or an
376       // Archive. In case we can successfully cast the member as an IRObject, it
377       // is safe to throw away the error generated due to casting the object as
378       // a MachOObjectFile.
379       consumeError(MachOObjOrErr.takeError());
380 
381       NewArchiveMember NewMember =
382           NewArchiveMember(IRObjectOrError->get()->getMemoryBufferRef());
383       NewMember.MemberName = sys::path::filename(NewMember.MemberName);
384 
385       if (Error E = verifyAndAddIRObject(Members, std::move(NewMember), C))
386         return E;
387       continue;
388     }
389 
390     Expected<std::unique_ptr<Archive>> ArchiveOrError = O.getAsArchive();
391     if (ArchiveOrError) {
392       // A universal file member can be a MachOObjectFile, an IRObject or an
393       // Archive. In case we can successfully cast the member as an Archive, it
394       // is safe to throw away the error generated due to casting the object as
395       // a MachOObjectFile.
396       consumeError(MachOObjOrErr.takeError());
397       consumeError(IRObjectOrError.takeError());
398 
399       if (Error E = processArchive(Members, **ArchiveOrError, FileName, C))
400         return E;
401       continue;
402     }
403 
404     Error CombinedError = joinErrors(
405         ArchiveOrError.takeError(),
406         joinErrors(IRObjectOrError.takeError(), MachOObjOrErr.takeError()));
407     return createFileError(FileName, std::move(CombinedError));
408   }
409 
410   // Update vector UniversalBuffers with the MemoryBuffers to transfer
411   // ownership.
412   UniversalBuffers.push_back(std::move(NM.Buf));
413   return Error::success();
414 }
415 
416 static Error addMember(MembersPerArchitectureMap &Members,
417                        std::vector<std::unique_ptr<MemoryBuffer>> &FileBuffers,
418                        StringRef FileName, const Config &C) {
419   Expected<NewArchiveMember> NMOrErr =
420       NewArchiveMember::getFile(FileName, C.Deterministic);
421   if (!NMOrErr)
422     return createFileError(FileName, NMOrErr.takeError());
423 
424   // For regular archives, use the basename of the object path for the member
425   // name.
426   NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName);
427   file_magic Magic = identify_magic(NMOrErr->Buf->getBuffer());
428 
429   // Flatten archives.
430   if (Magic == file_magic::archive)
431     return addArchiveMembers(Members, FileBuffers, std::move(*NMOrErr),
432                              FileName, C);
433 
434   // Flatten universal files.
435   if (Magic == file_magic::macho_universal_binary)
436     return addUniversalMembers(Members, FileBuffers, std::move(*NMOrErr),
437                                FileName, C);
438 
439   // Bitcode files.
440   if (Magic == file_magic::bitcode)
441     return verifyAndAddIRObject(Members, std::move(*NMOrErr), C);
442 
443   if (Error E = verifyAndAddMachOObject(Members, std::move(*NMOrErr), C))
444     return E;
445   return Error::success();
446 }
447 
448 static Expected<SmallVector<Slice, 2>>
449 buildSlices(ArrayRef<OwningBinary<Archive>> OutputBinaries) {
450   SmallVector<Slice, 2> Slices;
451 
452   for (const auto &OB : OutputBinaries) {
453     const Archive &A = *OB.getBinary();
454     Expected<Slice> ArchiveSlice = Slice::create(A, &LLVMCtx);
455     if (!ArchiveSlice)
456       return ArchiveSlice.takeError();
457     Slices.push_back(*ArchiveSlice);
458   }
459   return Slices;
460 }
461 
462 static Error createStaticLibrary(const Config &C) {
463   MembersPerArchitectureMap NewMembers;
464   std::vector<std::unique_ptr<MemoryBuffer>> FileBuffers;
465   for (StringRef FileName : InputFiles)
466     if (Error E = addMember(NewMembers, FileBuffers, FileName, C))
467       return E;
468 
469   if (!ArchType.empty()) {
470     uint64_t ArchCPUID = getCPUID(C.ArchCPUType, C.ArchCPUSubtype);
471     if (NewMembers.find(ArchCPUID) == NewMembers.end())
472       return createStringError(std::errc::invalid_argument,
473                                "no library created (no object files in input "
474                                "files matching -arch_only %s)",
475                                ArchType.c_str());
476   }
477 
478   if (NewMembers.size() == 1) {
479     if (Error E =
480             writeArchive(OutputFile, NewMembers.begin()->second,
481                          /*WriteSymtab=*/true,
482                          /*Kind=*/object::Archive::K_DARWIN, C.Deterministic,
483                          /*Thin=*/false))
484       return E;
485   } else {
486     SmallVector<OwningBinary<Archive>, 2> OutputBinaries;
487     for (const std::pair<const uint64_t, std::vector<NewArchiveMember>> &M :
488          NewMembers) {
489       Expected<std::unique_ptr<MemoryBuffer>> OutputBufferOrErr =
490           writeArchiveToBuffer(M.second,
491                                /*WriteSymtab=*/true,
492                                /*Kind=*/object::Archive::K_DARWIN,
493                                C.Deterministic,
494                                /*Thin=*/false);
495       if (!OutputBufferOrErr)
496         return OutputBufferOrErr.takeError();
497       std::unique_ptr<MemoryBuffer> &OutputBuffer = OutputBufferOrErr.get();
498 
499       Expected<std::unique_ptr<Archive>> ArchiveOrError =
500           Archive::create(OutputBuffer->getMemBufferRef());
501       if (!ArchiveOrError)
502         return ArchiveOrError.takeError();
503       std::unique_ptr<Archive> &A = ArchiveOrError.get();
504 
505       OutputBinaries.push_back(
506           OwningBinary<Archive>(std::move(A), std::move(OutputBuffer)));
507     }
508 
509     Expected<SmallVector<Slice, 2>> Slices = buildSlices(OutputBinaries);
510     if (!Slices)
511       return Slices.takeError();
512 
513     llvm::stable_sort(*Slices);
514     if (Error E = writeUniversalBinary(*Slices, OutputFile))
515       return E;
516   }
517   return Error::success();
518 }
519 
520 static Expected<Config> parseCommandLine(int Argc, char **Argv) {
521   Config C;
522   cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n");
523 
524   if (LibraryOperation == Operation::None) {
525     if (!VersionOption) {
526       std::string Error;
527       raw_string_ostream Stream(Error);
528       LibraryOperation.error("must be specified", "", Stream);
529       return createStringError(std::errc::invalid_argument, Error.c_str());
530     }
531     return C;
532   }
533 
534   if (OutputFile.empty()) {
535     std::string Error;
536     raw_string_ostream Stream(Error);
537     OutputFile.error("must be specified", "o", Stream);
538     return createStringError(std::errc::invalid_argument, Error.c_str());
539   }
540 
541   if (DeterministicOption && NonDeterministicOption)
542     return createStringError(std::errc::invalid_argument,
543                              "cannot specify both -D and -U flags");
544   else if (NonDeterministicOption)
545     C.Deterministic = false;
546 
547   if (!Libraries.empty())
548     if (Error E = processCommandLineLibraries())
549       return std::move(E);
550 
551   if (!FileList.empty())
552     if (Error E = processFileList())
553       return std::move(E);
554 
555   if (InputFiles.empty())
556     return createStringError(std::errc::invalid_argument,
557                              "no input files specified");
558 
559   if (ArchType.getNumOccurrences()) {
560     if (Error E = validateArchitectureName(ArchType))
561       return std::move(E);
562 
563     std::tie(C.ArchCPUType, C.ArchCPUSubtype) =
564         MachO::getCPUTypeFromArchitecture(
565             MachO::getArchitectureFromName(ArchType));
566   }
567 
568   return C;
569 }
570 
571 int main(int Argc, char **Argv) {
572   InitLLVM X(Argc, Argv);
573   cl::HideUnrelatedOptions({&LibtoolCategory, &ColorCategory});
574   Expected<Config> ConfigOrErr = parseCommandLine(Argc, Argv);
575   if (!ConfigOrErr) {
576     WithColor::defaultErrorHandler(ConfigOrErr.takeError());
577     return EXIT_FAILURE;
578   }
579 
580   if (VersionOption)
581     cl::PrintVersionMessage();
582 
583   Config C = *ConfigOrErr;
584   switch (LibraryOperation) {
585   case Operation::None:
586     break;
587   case Operation::Static:
588     if (Error E = createStaticLibrary(C)) {
589       WithColor::defaultErrorHandler(std::move(E));
590       return EXIT_FAILURE;
591     }
592     break;
593   }
594 }
595