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