//===- lib/Driver/DarwinLdDriver.cpp --------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// /// Concrete instance of the Driver for darwin's ld. /// //===----------------------------------------------------------------------===// #include "lld/Common/Args.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/LLVM.h" #include "lld/Core/ArchiveLibraryFile.h" #include "lld/Core/Error.h" #include "lld/Core/File.h" #include "lld/Core/Instrumentation.h" #include "lld/Core/LinkingContext.h" #include "lld/Core/Node.h" #include "lld/Core/PassManager.h" #include "lld/Core/Resolver.h" #include "lld/Core/SharedLibraryFile.h" #include "lld/Core/Simple.h" #include "lld/ReaderWriter/MachOLinkingContext.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptTable.h" #include "llvm/Option/Option.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Format.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include using namespace lld; namespace { // Create enum with OPT_xxx values for each option in DarwinLdOptions.td enum { OPT_INVALID = 0, #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELP, META, VALUES) \ OPT_##ID, #include "DarwinLdOptions.inc" #undef OPTION }; // Create prefix string literals used in DarwinLdOptions.td #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; #include "DarwinLdOptions.inc" #undef PREFIX // Create table mapping all options defined in DarwinLdOptions.td static const llvm::opt::OptTable::Info InfoTable[] = { #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ HELPTEXT, METAVAR, VALUES) \ {PREFIX, NAME, HELPTEXT, \ METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ PARAM, FLAGS, OPT_##GROUP, \ OPT_##ALIAS, ALIASARGS, VALUES}, #include "DarwinLdOptions.inc" #undef OPTION }; // Create OptTable class for parsing actual command line arguments class DarwinLdOptTable : public llvm::opt::OptTable { public: DarwinLdOptTable() : OptTable(InfoTable) {} }; static std::vector> makeErrorFile(StringRef path, std::error_code ec) { std::vector> result; result.push_back(std::make_unique(path, ec)); return result; } static std::vector> parseMemberFiles(std::unique_ptr file) { std::vector> members; if (auto *archive = dyn_cast(file.get())) { if (std::error_code ec = archive->parseAllMembers(members)) return makeErrorFile(file->path(), ec); } else { members.push_back(std::move(file)); } return members; } std::vector> loadFile(MachOLinkingContext &ctx, StringRef path, bool wholeArchive, bool upwardDylib) { if (ctx.logInputFiles()) message(path); ErrorOr> mbOrErr = ctx.getMemoryBuffer(path); if (std::error_code ec = mbOrErr.getError()) return makeErrorFile(path, ec); ErrorOr> fileOrErr = ctx.registry().loadFile(std::move(mbOrErr.get())); if (std::error_code ec = fileOrErr.getError()) return makeErrorFile(path, ec); std::unique_ptr &file = fileOrErr.get(); // If file is a dylib, inform LinkingContext about it. if (SharedLibraryFile *shl = dyn_cast(file.get())) { if (std::error_code ec = shl->parse()) return makeErrorFile(path, ec); ctx.registerDylib(reinterpret_cast(shl), upwardDylib); } if (wholeArchive) return parseMemberFiles(std::move(file)); std::vector> files; files.push_back(std::move(file)); return files; } } // end anonymous namespace // Test may be running on Windows. Canonicalize the path // separator to '/' to get consistent outputs for tests. static std::string canonicalizePath(StringRef path) { char sep = llvm::sys::path::get_separator().front(); if (sep != '/') { std::string fixedPath = std::string(path); std::replace(fixedPath.begin(), fixedPath.end(), sep, '/'); return fixedPath; } else { return std::string(path); } } static void addFile(StringRef path, MachOLinkingContext &ctx, bool loadWholeArchive, bool upwardDylib) { std::vector> files = loadFile(ctx, path, loadWholeArchive, upwardDylib); for (std::unique_ptr &file : files) ctx.getNodes().push_back(std::make_unique(std::move(file))); } // Export lists are one symbol per line. Blank lines are ignored. // Trailing comments start with #. static std::error_code parseExportsList(StringRef exportFilePath, MachOLinkingContext &ctx) { // Map in export list file. ErrorOr> mb = MemoryBuffer::getFileOrSTDIN(exportFilePath); if (std::error_code ec = mb.getError()) return ec; ctx.addInputFileDependency(exportFilePath); StringRef buffer = mb->get()->getBuffer(); while (!buffer.empty()) { // Split off each line in the file. std::pair lineAndRest = buffer.split('\n'); StringRef line = lineAndRest.first; // Ignore trailing # comments. std::pair symAndComment = line.split('#'); StringRef sym = symAndComment.first.trim(); if (!sym.empty()) ctx.addExportSymbol(sym); buffer = lineAndRest.second; } return std::error_code(); } /// Order files are one symbol per line. Blank lines are ignored. /// Trailing comments start with #. Symbol names can be prefixed with an /// architecture name and/or .o leaf name. Examples: /// _foo /// bar.o:_bar /// libfrob.a(bar.o):_bar /// x86_64:_foo64 static std::error_code parseOrderFile(StringRef orderFilePath, MachOLinkingContext &ctx) { // Map in order file. ErrorOr> mb = MemoryBuffer::getFileOrSTDIN(orderFilePath); if (std::error_code ec = mb.getError()) return ec; ctx.addInputFileDependency(orderFilePath); StringRef buffer = mb->get()->getBuffer(); while (!buffer.empty()) { // Split off each line in the file. std::pair lineAndRest = buffer.split('\n'); StringRef line = lineAndRest.first; buffer = lineAndRest.second; // Ignore trailing # comments. std::pair symAndComment = line.split('#'); if (symAndComment.first.empty()) continue; StringRef sym = symAndComment.first.trim(); if (sym.empty()) continue; // Check for prefix. StringRef prefix; std::pair prefixAndSym = sym.split(':'); if (!prefixAndSym.second.empty()) { sym = prefixAndSym.second; prefix = prefixAndSym.first; if (!prefix.endswith(".o") && !prefix.endswith(".o)")) { // If arch name prefix does not match arch being linked, ignore symbol. if (!ctx.archName().equals(prefix)) continue; prefix = ""; } } else sym = prefixAndSym.first; if (!sym.empty()) { ctx.appendOrderedSymbol(sym, prefix); // llvm::errs() << sym << ", prefix=" << prefix << "\n"; } } return std::error_code(); } // // There are two variants of the -filelist option: // // -filelist // In this variant, the path is to a text file which contains one file path // per line. There are no comments or trimming of whitespace. // // -fileList , // In this variant, the path is to a text file which contains a partial path // per line. The prefix is prepended to each partial path. // static llvm::Error loadFileList(StringRef fileListPath, MachOLinkingContext &ctx, bool forceLoad) { // If there is a comma, split off . std::pair opt = fileListPath.split(','); StringRef filePath = opt.first; StringRef dirName = opt.second; ctx.addInputFileDependency(filePath); // Map in file list file. ErrorOr> mb = MemoryBuffer::getFileOrSTDIN(filePath); if (std::error_code ec = mb.getError()) return llvm::errorCodeToError(ec); StringRef buffer = mb->get()->getBuffer(); while (!buffer.empty()) { // Split off each line in the file. std::pair lineAndRest = buffer.split('\n'); StringRef line = lineAndRest.first; StringRef path; if (!dirName.empty()) { // If there is a then prepend dir to each line. SmallString<256> fullPath; fullPath.assign(dirName); llvm::sys::path::append(fullPath, Twine(line)); path = ctx.copy(fullPath.str()); } else { // No use whole line as input file path. path = ctx.copy(line); } if (!ctx.pathExists(path)) { return llvm::make_error(Twine("File not found '") + path + "'"); } if (ctx.testingFileUsage()) { message("Found filelist entry " + canonicalizePath(path)); } addFile(path, ctx, forceLoad, false); buffer = lineAndRest.second; } return llvm::Error::success(); } /// Parse number assuming it is base 16, but allow 0x prefix. static bool parseNumberBase16(StringRef numStr, uint64_t &baseAddress) { if (numStr.startswith_lower("0x")) numStr = numStr.drop_front(2); return numStr.getAsInteger(16, baseAddress); } static void parseLLVMOptions(const LinkingContext &ctx) { // Honor -mllvm if (!ctx.llvmOptions().empty()) { unsigned numArgs = ctx.llvmOptions().size(); auto **args = new const char *[numArgs + 2]; args[0] = "lld (LLVM option parsing)"; for (unsigned i = 0; i != numArgs; ++i) args[i + 1] = ctx.llvmOptions()[i]; args[numArgs + 1] = nullptr; llvm::cl::ResetAllOptionOccurrences(); llvm::cl::ParseCommandLineOptions(numArgs + 1, args); } } namespace lld { namespace mach_o { bool parse(llvm::ArrayRef args, MachOLinkingContext &ctx) { // Parse command line options using DarwinLdOptions.td DarwinLdOptTable table; unsigned missingIndex; unsigned missingCount; llvm::opt::InputArgList parsedArgs = table.ParseArgs(args.slice(1), missingIndex, missingCount); if (missingCount) { error("missing arg value for '" + Twine(parsedArgs.getArgString(missingIndex)) + "' expected " + Twine(missingCount) + " argument(s)."); return false; } for (auto unknownArg : parsedArgs.filtered(OPT_UNKNOWN)) { warn("ignoring unknown argument: " + Twine(unknownArg->getAsString(parsedArgs))); } errorHandler().verbose = parsedArgs.hasArg(OPT_v); errorHandler().errorLimit = args::getInteger(parsedArgs, OPT_error_limit, 20); // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static ) llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE; bool isStaticExecutable = false; if (llvm::opt::Arg *kind = parsedArgs.getLastArg( OPT_dylib, OPT_relocatable, OPT_bundle, OPT_static, OPT_preload)) { switch (kind->getOption().getID()) { case OPT_dylib: fileType = llvm::MachO::MH_DYLIB; break; case OPT_relocatable: fileType = llvm::MachO::MH_OBJECT; break; case OPT_bundle: fileType = llvm::MachO::MH_BUNDLE; break; case OPT_static: fileType = llvm::MachO::MH_EXECUTE; isStaticExecutable = true; break; case OPT_preload: fileType = llvm::MachO::MH_PRELOAD; break; } } // Handle -arch xxx MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown; if (llvm::opt::Arg *archStr = parsedArgs.getLastArg(OPT_arch)) { arch = MachOLinkingContext::archFromName(archStr->getValue()); if (arch == MachOLinkingContext::arch_unknown) { error("unknown arch named '" + Twine(archStr->getValue()) + "'"); return false; } } // If no -arch specified, scan input files to find first non-fat .o file. if (arch == MachOLinkingContext::arch_unknown) { for (auto &inFile : parsedArgs.filtered(OPT_INPUT)) { // This is expensive because it opens and maps the file. But that is // ok because no -arch is rare. if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch)) break; } if (arch == MachOLinkingContext::arch_unknown && !parsedArgs.getLastArg(OPT_test_file_usage)) { // If no -arch and no options at all, print usage message. if (parsedArgs.size() == 0) { table.PrintHelp(llvm::outs(), (std::string(args[0]) + " [options] file...").c_str(), "LLVM Linker", false); } else { error("-arch not specified and could not be inferred"); } return false; } } // Handle -macosx_version_min or -ios_version_min MachOLinkingContext::OS os = MachOLinkingContext::OS::unknown; uint32_t minOSVersion = 0; if (llvm::opt::Arg *minOS = parsedArgs.getLastArg(OPT_macosx_version_min, OPT_ios_version_min, OPT_ios_simulator_version_min)) { switch (minOS->getOption().getID()) { case OPT_macosx_version_min: os = MachOLinkingContext::OS::macOSX; if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), minOSVersion)) { error("malformed macosx_version_min value"); return false; } break; case OPT_ios_version_min: os = MachOLinkingContext::OS::iOS; if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), minOSVersion)) { error("malformed ios_version_min value"); return false; } break; case OPT_ios_simulator_version_min: os = MachOLinkingContext::OS::iOS_simulator; if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), minOSVersion)) { error("malformed ios_simulator_version_min value"); return false; } break; } } else { // No min-os version on command line, check environment variables } // Handle export_dynamic // FIXME: Should we warn when this applies to something other than a static // executable or dylib? Those are the only cases where this has an effect. // Note, this has to come before ctx.configure() so that we get the correct // value for _globalsAreDeadStripRoots. bool exportDynamicSymbols = parsedArgs.hasArg(OPT_export_dynamic); // Now that there's enough information parsed in, let the linking context // set up default values. ctx.configure(fileType, arch, os, minOSVersion, exportDynamicSymbols); // Handle -e xxx if (llvm::opt::Arg *entry = parsedArgs.getLastArg(OPT_entry)) ctx.setEntrySymbolName(entry->getValue()); // Handle -o xxx if (llvm::opt::Arg *outpath = parsedArgs.getLastArg(OPT_output)) ctx.setOutputPath(outpath->getValue()); else ctx.setOutputPath("a.out"); // Handle -image_base XXX and -seg1addr XXXX if (llvm::opt::Arg *imageBase = parsedArgs.getLastArg(OPT_image_base)) { uint64_t baseAddress; if (parseNumberBase16(imageBase->getValue(), baseAddress)) { error("image_base expects a hex number"); return false; } else if (baseAddress < ctx.pageZeroSize()) { error("image_base overlaps with __PAGEZERO"); return false; } else if (baseAddress % ctx.pageSize()) { error("image_base must be a multiple of page size (0x" + llvm::utohexstr(ctx.pageSize()) + ")"); return false; } ctx.setBaseAddress(baseAddress); } // Handle -dead_strip if (parsedArgs.getLastArg(OPT_dead_strip)) ctx.setDeadStripping(true); bool globalWholeArchive = false; // Handle -all_load if (parsedArgs.getLastArg(OPT_all_load)) globalWholeArchive = true; // Handle -install_name if (llvm::opt::Arg *installName = parsedArgs.getLastArg(OPT_install_name)) ctx.setInstallName(installName->getValue()); else ctx.setInstallName(ctx.outputPath()); // Handle -mark_dead_strippable_dylib if (parsedArgs.getLastArg(OPT_mark_dead_strippable_dylib)) ctx.setDeadStrippableDylib(true); // Handle -compatibility_version and -current_version if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_compatibility_version)) { if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { error("-compatibility_version can only be used with -dylib"); return false; } uint32_t parsedVers; if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { error("-compatibility_version value is malformed"); return false; } ctx.setCompatibilityVersion(parsedVers); } if (llvm::opt::Arg *vers = parsedArgs.getLastArg(OPT_current_version)) { if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { error("-current_version can only be used with -dylib"); return false; } uint32_t parsedVers; if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { error("-current_version value is malformed"); return false; } ctx.setCurrentVersion(parsedVers); } // Handle -bundle_loader if (llvm::opt::Arg *loader = parsedArgs.getLastArg(OPT_bundle_loader)) ctx.setBundleLoader(loader->getValue()); // Handle -sectalign segname sectname align for (auto &alignArg : parsedArgs.filtered(OPT_sectalign)) { const char* segName = alignArg->getValue(0); const char* sectName = alignArg->getValue(1); const char* alignStr = alignArg->getValue(2); if ((alignStr[0] == '0') && (alignStr[1] == 'x')) alignStr += 2; unsigned long long alignValue; if (llvm::getAsUnsignedInteger(alignStr, 16, alignValue)) { error("-sectalign alignment value '" + Twine(alignStr) + "' not a valid number"); return false; } uint16_t align = 1 << llvm::countTrailingZeros(alignValue); if (!llvm::isPowerOf2_64(alignValue)) { std::string Msg; llvm::raw_string_ostream OS(Msg); OS << "alignment for '-sectalign " << segName << " " << sectName << llvm::format(" 0x%llX", alignValue) << "' is not a power of two, using " << llvm::format("0x%08X", align); OS.flush(); warn(Msg); } ctx.addSectionAlignment(segName, sectName, align); } // Handle -mllvm for (auto &llvmArg : parsedArgs.filtered(OPT_mllvm)) { ctx.appendLLVMOption(llvmArg->getValue()); } // Handle -print_atoms if (parsedArgs.getLastArg(OPT_print_atoms)) ctx.setPrintAtoms(); // Handle -t (trace) option. if (parsedArgs.getLastArg(OPT_t)) ctx.setLogInputFiles(true); // Handle -demangle option. if (parsedArgs.getLastArg(OPT_demangle)) ctx.setDemangleSymbols(true); // Handle -keep_private_externs if (parsedArgs.getLastArg(OPT_keep_private_externs)) { ctx.setKeepPrivateExterns(true); if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT) warn("-keep_private_externs only used in -r mode"); } // Handle -dependency_info used by Xcode. if (llvm::opt::Arg *depInfo = parsedArgs.getLastArg(OPT_dependency_info)) if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) warn(ec.message() + ", processing '-dependency_info " + depInfo->getValue()); // In -test_file_usage mode, we'll be given an explicit list of paths that // exist. We'll also be expected to print out information about how we located // libraries and so on that the user specified, but not to actually do any // linking. if (parsedArgs.getLastArg(OPT_test_file_usage)) { ctx.setTestingFileUsage(); // With paths existing by fiat, linking is not going to end well. ctx.setDoNothing(true); // Only bother looking for an existence override if we're going to use it. for (auto existingPath : parsedArgs.filtered(OPT_path_exists)) { ctx.addExistingPathForDebug(existingPath->getValue()); } } // Register possible input file parsers. if (!ctx.doNothing()) { ctx.registry().addSupportMachOObjects(ctx); ctx.registry().addSupportArchives(ctx.logInputFiles()); ctx.registry().addSupportYamlFiles(); } // Now construct the set of library search directories, following ld64's // baroque set of accumulated hacks. Mostly, the algorithm constructs // { syslibroots } x { libpaths } // // Unfortunately, there are numerous exceptions: // 1. Only absolute paths get modified by syslibroot options. // 2. If there is just 1 -syslibroot, system paths not found in it are // skipped. // 3. If the last -syslibroot is "/", all of them are ignored entirely. // 4. If { syslibroots } x path == {}, the original path is kept. std::vector sysLibRoots; for (auto syslibRoot : parsedArgs.filtered(OPT_syslibroot)) { sysLibRoots.push_back(syslibRoot->getValue()); } if (!sysLibRoots.empty()) { // Ignore all if last -syslibroot is "/". if (sysLibRoots.back() != "/") ctx.setSysLibRoots(sysLibRoots); } // Paths specified with -L come first, and are not considered system paths for // the case where there is precisely 1 -syslibroot. for (auto libPath : parsedArgs.filtered(OPT_L)) { ctx.addModifiedSearchDir(libPath->getValue()); } // Process -F directories (where to look for frameworks). for (auto fwPath : parsedArgs.filtered(OPT_F)) { ctx.addFrameworkSearchDir(fwPath->getValue()); } // -Z suppresses the standard search paths. if (!parsedArgs.hasArg(OPT_Z)) { ctx.addModifiedSearchDir("/usr/lib", true); ctx.addModifiedSearchDir("/usr/local/lib", true); ctx.addFrameworkSearchDir("/Library/Frameworks", true); ctx.addFrameworkSearchDir("/System/Library/Frameworks", true); } // Now that we've constructed the final set of search paths, print out those // search paths in verbose mode. if (errorHandler().verbose) { message("Library search paths:"); for (auto path : ctx.searchDirs()) { message(" " + path); } message("Framework search paths:"); for (auto path : ctx.frameworkDirs()) { message(" " + path); } } // Handle -exported_symbols_list for (auto expFile : parsedArgs.filtered(OPT_exported_symbols_list)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) { error("-exported_symbols_list cannot be combined with " "-unexported_symbol[s_list]"); return false; } ctx.setExportMode(MachOLinkingContext::ExportMode::exported); if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) { error(ec.message() + ", processing '-exported_symbols_list " + expFile->getValue()); return false; } } // Handle -exported_symbol for (auto symbol : parsedArgs.filtered(OPT_exported_symbol)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) { error("-exported_symbol cannot be combined with " "-unexported_symbol[s_list]"); return false; } ctx.setExportMode(MachOLinkingContext::ExportMode::exported); ctx.addExportSymbol(symbol->getValue()); } // Handle -unexported_symbols_list for (auto expFile : parsedArgs.filtered(OPT_unexported_symbols_list)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) { error("-unexported_symbols_list cannot be combined with " "-exported_symbol[s_list]"); return false; } ctx.setExportMode(MachOLinkingContext::ExportMode::unexported); if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) { error(ec.message() + ", processing '-unexported_symbols_list " + expFile->getValue()); return false; } } // Handle -unexported_symbol for (auto symbol : parsedArgs.filtered(OPT_unexported_symbol)) { if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) { error("-unexported_symbol cannot be combined with " "-exported_symbol[s_list]"); return false; } ctx.setExportMode(MachOLinkingContext::ExportMode::unexported); ctx.addExportSymbol(symbol->getValue()); } // Handle obosolete -multi_module and -single_module if (llvm::opt::Arg *mod = parsedArgs.getLastArg(OPT_multi_module, OPT_single_module)) { if (mod->getOption().getID() == OPT_multi_module) warn("-multi_module is obsolete and being ignored"); else if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) warn("-single_module being ignored. It is only for use when producing a " "dylib"); } // Handle obsolete ObjC options: -objc_gc_compaction, -objc_gc, -objc_gc_only if (parsedArgs.getLastArg(OPT_objc_gc_compaction)) { error("-objc_gc_compaction is not supported"); return false; } if (parsedArgs.getLastArg(OPT_objc_gc)) { error("-objc_gc is not supported"); return false; } if (parsedArgs.getLastArg(OPT_objc_gc_only)) { error("-objc_gc_only is not supported"); return false; } // Handle -pie or -no_pie if (llvm::opt::Arg *pie = parsedArgs.getLastArg(OPT_pie, OPT_no_pie)) { switch (ctx.outputMachOType()) { case llvm::MachO::MH_EXECUTE: switch (ctx.os()) { case MachOLinkingContext::OS::macOSX: if ((minOSVersion < 0x000A0500) && (pie->getOption().getID() == OPT_pie)) { error("-pie can only be used when targeting Mac OS X 10.5 or later"); return false; } break; case MachOLinkingContext::OS::iOS: if ((minOSVersion < 0x00040200) && (pie->getOption().getID() == OPT_pie)) { error("-pie can only be used when targeting iOS 4.2 or later"); return false; } break; case MachOLinkingContext::OS::iOS_simulator: if (pie->getOption().getID() == OPT_no_pie) { error("iOS simulator programs must be built PIE"); return false; } break; case MachOLinkingContext::OS::unknown: break; } ctx.setPIE(pie->getOption().getID() == OPT_pie); break; case llvm::MachO::MH_PRELOAD: break; case llvm::MachO::MH_DYLIB: case llvm::MachO::MH_BUNDLE: warn(pie->getSpelling() + " being ignored. It is only used when linking main executables"); break; default: error(pie->getSpelling() + " can only used when linking main executables"); return false; } } // Handle -version_load_command or -no_version_load_command { bool flagOn = false; bool flagOff = false; if (auto *arg = parsedArgs.getLastArg(OPT_version_load_command, OPT_no_version_load_command)) { flagOn = arg->getOption().getID() == OPT_version_load_command; flagOff = arg->getOption().getID() == OPT_no_version_load_command; } // default to adding version load command for dynamic code, // static code must opt-in switch (ctx.outputMachOType()) { case llvm::MachO::MH_OBJECT: ctx.setGenerateVersionLoadCommand(false); break; case llvm::MachO::MH_EXECUTE: // dynamic executables default to generating a version load command, // while static executables only generate it if required. if (isStaticExecutable) { if (flagOn) ctx.setGenerateVersionLoadCommand(true); } else { if (!flagOff) ctx.setGenerateVersionLoadCommand(true); } break; case llvm::MachO::MH_PRELOAD: case llvm::MachO::MH_KEXT_BUNDLE: if (flagOn) ctx.setGenerateVersionLoadCommand(true); break; case llvm::MachO::MH_DYLINKER: case llvm::MachO::MH_DYLIB: case llvm::MachO::MH_BUNDLE: if (!flagOff) ctx.setGenerateVersionLoadCommand(true); break; case llvm::MachO::MH_FVMLIB: case llvm::MachO::MH_DYLDLINK: case llvm::MachO::MH_DYLIB_STUB: case llvm::MachO::MH_DSYM: // We don't generate load commands for these file types, even if // forced on. break; } } // Handle -function_starts or -no_function_starts { bool flagOn = false; bool flagOff = false; if (auto *arg = parsedArgs.getLastArg(OPT_function_starts, OPT_no_function_starts)) { flagOn = arg->getOption().getID() == OPT_function_starts; flagOff = arg->getOption().getID() == OPT_no_function_starts; } // default to adding functions start for dynamic code, static code must // opt-in switch (ctx.outputMachOType()) { case llvm::MachO::MH_OBJECT: ctx.setGenerateFunctionStartsLoadCommand(false); break; case llvm::MachO::MH_EXECUTE: // dynamic executables default to generating a version load command, // while static executables only generate it if required. if (isStaticExecutable) { if (flagOn) ctx.setGenerateFunctionStartsLoadCommand(true); } else { if (!flagOff) ctx.setGenerateFunctionStartsLoadCommand(true); } break; case llvm::MachO::MH_PRELOAD: case llvm::MachO::MH_KEXT_BUNDLE: if (flagOn) ctx.setGenerateFunctionStartsLoadCommand(true); break; case llvm::MachO::MH_DYLINKER: case llvm::MachO::MH_DYLIB: case llvm::MachO::MH_BUNDLE: if (!flagOff) ctx.setGenerateFunctionStartsLoadCommand(true); break; case llvm::MachO::MH_FVMLIB: case llvm::MachO::MH_DYLDLINK: case llvm::MachO::MH_DYLIB_STUB: case llvm::MachO::MH_DSYM: // We don't generate load commands for these file types, even if // forced on. break; } } // Handle -data_in_code_info or -no_data_in_code_info { bool flagOn = false; bool flagOff = false; if (auto *arg = parsedArgs.getLastArg(OPT_data_in_code_info, OPT_no_data_in_code_info)) { flagOn = arg->getOption().getID() == OPT_data_in_code_info; flagOff = arg->getOption().getID() == OPT_no_data_in_code_info; } // default to adding data in code for dynamic code, static code must // opt-in switch (ctx.outputMachOType()) { case llvm::MachO::MH_OBJECT: if (!flagOff) ctx.setGenerateDataInCodeLoadCommand(true); break; case llvm::MachO::MH_EXECUTE: // dynamic executables default to generating a version load command, // while static executables only generate it if required. if (isStaticExecutable) { if (flagOn) ctx.setGenerateDataInCodeLoadCommand(true); } else { if (!flagOff) ctx.setGenerateDataInCodeLoadCommand(true); } break; case llvm::MachO::MH_PRELOAD: case llvm::MachO::MH_KEXT_BUNDLE: if (flagOn) ctx.setGenerateDataInCodeLoadCommand(true); break; case llvm::MachO::MH_DYLINKER: case llvm::MachO::MH_DYLIB: case llvm::MachO::MH_BUNDLE: if (!flagOff) ctx.setGenerateDataInCodeLoadCommand(true); break; case llvm::MachO::MH_FVMLIB: case llvm::MachO::MH_DYLDLINK: case llvm::MachO::MH_DYLIB_STUB: case llvm::MachO::MH_DSYM: // We don't generate load commands for these file types, even if // forced on. break; } } // Handle sdk_version if (llvm::opt::Arg *arg = parsedArgs.getLastArg(OPT_sdk_version)) { uint32_t sdkVersion = 0; if (MachOLinkingContext::parsePackedVersion(arg->getValue(), sdkVersion)) { error("malformed sdkVersion value"); return false; } ctx.setSdkVersion(sdkVersion); } else if (ctx.generateVersionLoadCommand()) { // If we don't have an sdk version, but were going to emit a load command // with min_version, then we need to give a warning as we have no sdk // version to put in that command. // FIXME: We need to decide whether to make this an error. warn("-sdk_version is required when emitting min version load command. " "Setting sdk version to match provided min version"); ctx.setSdkVersion(ctx.osMinVersion()); } // Handle source_version if (llvm::opt::Arg *arg = parsedArgs.getLastArg(OPT_source_version)) { uint64_t version = 0; if (MachOLinkingContext::parsePackedVersion(arg->getValue(), version)) { error("malformed source_version value"); return false; } ctx.setSourceVersion(version); } // Handle stack_size if (llvm::opt::Arg *stackSize = parsedArgs.getLastArg(OPT_stack_size)) { uint64_t stackSizeVal; if (parseNumberBase16(stackSize->getValue(), stackSizeVal)) { error("stack_size expects a hex number"); return false; } if ((stackSizeVal % ctx.pageSize()) != 0) { error("stack_size must be a multiple of page size (0x" + llvm::utohexstr(ctx.pageSize()) + ")"); return false; } ctx.setStackSize(stackSizeVal); } // Handle debug info handling options: -S if (parsedArgs.hasArg(OPT_S)) ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap); // Handle -order_file for (auto orderFile : parsedArgs.filtered(OPT_order_file)) { if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx)) { error(ec.message() + ", processing '-order_file " + orderFile->getValue() + "'"); return false; } } // Handle -flat_namespace. if (llvm::opt::Arg *ns = parsedArgs.getLastArg(OPT_flat_namespace, OPT_twolevel_namespace)) { if (ns->getOption().getID() == OPT_flat_namespace) ctx.setUseFlatNamespace(true); } // Handle -undefined if (llvm::opt::Arg *undef = parsedArgs.getLastArg(OPT_undefined)) { MachOLinkingContext::UndefinedMode UndefMode; if (StringRef(undef->getValue()).equals("error")) UndefMode = MachOLinkingContext::UndefinedMode::error; else if (StringRef(undef->getValue()).equals("warning")) UndefMode = MachOLinkingContext::UndefinedMode::warning; else if (StringRef(undef->getValue()).equals("suppress")) UndefMode = MachOLinkingContext::UndefinedMode::suppress; else if (StringRef(undef->getValue()).equals("dynamic_lookup")) UndefMode = MachOLinkingContext::UndefinedMode::dynamicLookup; else { error("invalid option to -undefined [ warning | error | suppress | " "dynamic_lookup ]"); return false; } if (ctx.useFlatNamespace()) { // If we're using -flat_namespace then 'warning', 'suppress' and // 'dynamic_lookup' are all equivalent, so map them to 'suppress'. if (UndefMode != MachOLinkingContext::UndefinedMode::error) UndefMode = MachOLinkingContext::UndefinedMode::suppress; } else { // If we're using -twolevel_namespace then 'warning' and 'suppress' are // illegal. Emit a diagnostic if they've been (mis)used. if (UndefMode == MachOLinkingContext::UndefinedMode::warning || UndefMode == MachOLinkingContext::UndefinedMode::suppress) { error("can't use -undefined warning or suppress with " "-twolevel_namespace"); return false; } } ctx.setUndefinedMode(UndefMode); } // Handle -no_objc_category_merging. if (parsedArgs.getLastArg(OPT_no_objc_category_merging)) ctx.setMergeObjCCategories(false); // Handle -rpath if (parsedArgs.hasArg(OPT_rpath)) { switch (ctx.outputMachOType()) { case llvm::MachO::MH_EXECUTE: case llvm::MachO::MH_DYLIB: case llvm::MachO::MH_BUNDLE: if (!ctx.minOS("10.5", "2.0")) { if (ctx.os() == MachOLinkingContext::OS::macOSX) error("-rpath can only be used when targeting OS X 10.5 or later"); else error("-rpath can only be used when targeting iOS 2.0 or later"); return false; } break; default: error("-rpath can only be used when creating a dynamic final linked " "image"); return false; } for (auto rPath : parsedArgs.filtered(OPT_rpath)) { ctx.addRpath(rPath->getValue()); } } // Parse the LLVM options before we process files in case the file handling // makes use of things like LLVM_DEBUG(). parseLLVMOptions(ctx); // Handle input files and sectcreate. for (auto &arg : parsedArgs) { bool upward; llvm::Optional resolvedPath; switch (arg->getOption().getID()) { default: continue; case OPT_INPUT: addFile(arg->getValue(), ctx, globalWholeArchive, false); break; case OPT_upward_library: addFile(arg->getValue(), ctx, false, true); break; case OPT_force_load: addFile(arg->getValue(), ctx, true, false); break; case OPT_l: case OPT_upward_l: upward = (arg->getOption().getID() == OPT_upward_l); resolvedPath = ctx.searchLibrary(arg->getValue()); if (!resolvedPath) { error("Unable to find library for " + arg->getSpelling() + arg->getValue()); return false; } else if (ctx.testingFileUsage()) { message(Twine("Found ") + (upward ? "upward " : " ") + "library " + canonicalizePath(resolvedPath.getValue())); } addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward); break; case OPT_framework: case OPT_upward_framework: upward = (arg->getOption().getID() == OPT_upward_framework); resolvedPath = ctx.findPathForFramework(arg->getValue()); if (!resolvedPath) { error("Unable to find framework for " + arg->getSpelling() + " " + arg->getValue()); return false; } else if (ctx.testingFileUsage()) { message(Twine("Found ") + (upward ? "upward " : " ") + "framework " + canonicalizePath(resolvedPath.getValue())); } addFile(resolvedPath.getValue(), ctx, globalWholeArchive, upward); break; case OPT_filelist: if (auto ec = loadFileList(arg->getValue(), ctx, globalWholeArchive)) { handleAllErrors(std::move(ec), [&](const llvm::ErrorInfoBase &EI) { error(EI.message() + ", processing '-filelist " + arg->getValue()); }); return false; } break; case OPT_sectcreate: { const char* seg = arg->getValue(0); const char* sect = arg->getValue(1); const char* fileName = arg->getValue(2); ErrorOr> contentOrErr = MemoryBuffer::getFile(fileName); if (!contentOrErr) { error("can't open -sectcreate file " + Twine(fileName)); return false; } ctx.addSectCreateSection(seg, sect, std::move(*contentOrErr)); } break; } } if (ctx.getNodes().empty()) { error("No input files"); return false; } // Validate the combination of options used. return ctx.validate(); } static void createFiles(MachOLinkingContext &ctx, bool Implicit) { std::vector> Files; if (Implicit) ctx.createImplicitFiles(Files); else ctx.createInternalFiles(Files); for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) { auto &members = ctx.getNodes(); members.insert(members.begin(), std::make_unique(std::move(*i))); } } /// This is where the link is actually performed. bool link(llvm::ArrayRef args, bool CanExitEarly, raw_ostream &StdoutOS, raw_ostream &StderrOS) { lld::stdoutOS = &StdoutOS; lld::stderrOS = &StderrOS; errorHandler().logName = args::getFilenameWithoutExe(args[0]); errorHandler().errorLimitExceededMsg = "too many errors emitted, stopping now (use " "'-error-limit 0' to see all errors)"; errorHandler().exitEarly = CanExitEarly; StderrOS.enable_colors(StderrOS.has_colors()); MachOLinkingContext ctx; if (!parse(args, ctx)) return false; if (ctx.doNothing()) return true; if (ctx.getNodes().empty()) return false; for (std::unique_ptr &ie : ctx.getNodes()) if (FileNode *node = dyn_cast(ie.get())) node->getFile()->parse(); createFiles(ctx, false /* Implicit */); // Give target a chance to add files createFiles(ctx, true /* Implicit */); // Give target a chance to postprocess input files. // Mach-O uses this chance to move all object files before library files. ctx.finalizeInputFiles(); // Do core linking. ScopedTask resolveTask(getDefaultDomain(), "Resolve"); Resolver resolver(ctx); if (!resolver.resolve()) return false; SimpleFile *merged = nullptr; { std::unique_ptr mergedFile = resolver.resultFile(); merged = mergedFile.get(); auto &members = ctx.getNodes(); members.insert(members.begin(), std::make_unique(std::move(mergedFile))); } resolveTask.end(); // Run passes on linked atoms. ScopedTask passTask(getDefaultDomain(), "Passes"); PassManager pm; ctx.addPasses(pm); if (auto ec = pm.runOnFile(*merged)) { // FIXME: This should be passed to logAllUnhandledErrors but it needs // to be passed a Twine instead of a string. lld::errs() << "Failed to run passes on file '" << ctx.outputPath() << "': "; logAllUnhandledErrors(std::move(ec), lld::errs(), std::string()); return false; } passTask.end(); // Give linked atoms to Writer to generate output file. ScopedTask writeTask(getDefaultDomain(), "Write"); if (auto ec = ctx.writeFile(*merged)) { // FIXME: This should be passed to logAllUnhandledErrors but it needs // to be passed a Twine instead of a string. lld::errs() << "Failed to write file '" << ctx.outputPath() << "': "; logAllUnhandledErrors(std::move(ec), lld::errs(), std::string()); return false; } // Call exit() if we can to avoid calling destructors. if (CanExitEarly) exitLld(errorCount() ? 1 : 0); return true; } } // end namespace mach_o } // end namespace lld