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