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