| //===- lib/Driver/DarwinLdDriver.cpp --------------------------------------===// |
| // |
| // The LLVM Linker |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// |
| /// Concrete instance of the Driver for darwin's ld. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "lld/Driver/Driver.h" |
| #include "lld/Driver/DarwinInputGraph.h" |
| #include "lld/ReaderWriter/MachOLinkingContext.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/Triple.h" |
| #include "llvm/Option/Arg.h" |
| #include "llvm/Option/Option.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/Host.h" |
| #include "llvm/Support/MachO.h" |
| #include "llvm/Support/ManagedStatic.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/PrettyStackTrace.h" |
| #include "llvm/Support/Signals.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <algorithm> |
| |
| 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) \ |
| 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) \ |
| { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \ |
| PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS }, |
| #include "DarwinLdOptions.inc" |
| #undef OPTION |
| }; |
| |
| // Create OptTable class for parsing actual command line arguments |
| class DarwinLdOptTable : public llvm::opt::OptTable { |
| public: |
| DarwinLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){} |
| }; |
| |
| } // 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 = path; |
| std::replace(fixedPath.begin(), fixedPath.end(), sep, '/'); |
| return fixedPath; |
| } else { |
| return path; |
| } |
| } |
| |
| static void addFile(StringRef path, std::unique_ptr<InputGraph> &inputGraph, |
| MachOLinkingContext &ctx, bool loadWholeArchive, |
| bool upwardDylib) { |
| auto node = llvm::make_unique<MachOFileNode>(path, ctx); |
| if (loadWholeArchive) |
| node->setLoadWholeArchive(); |
| if (upwardDylib) |
| node->setUpwardDylib(); |
| inputGraph->addInputElement(std::move(node)); |
| } |
| |
| // Export lists are one symbol per line. Blank lines are ignored. |
| // Trailing comments start with #. |
| static std::error_code parseExportsList(StringRef exportFilePath, |
| MachOLinkingContext &ctx, |
| raw_ostream &diagnostics) { |
| // Map in export list file. |
| ErrorOr<std::unique_ptr<MemoryBuffer>> 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<StringRef, StringRef> lineAndRest = buffer.split('\n'); |
| StringRef line = lineAndRest.first; |
| // Ignore trailing # comments. |
| std::pair<StringRef, StringRef> 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, |
| raw_ostream &diagnostics) { |
| // Map in order file. |
| ErrorOr<std::unique_ptr<MemoryBuffer>> 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<StringRef, StringRef> lineAndRest = buffer.split('\n'); |
| StringRef line = lineAndRest.first; |
| buffer = lineAndRest.second; |
| // Ignore trailing # comments. |
| std::pair<StringRef, StringRef> symAndComment = line.split('#'); |
| if (symAndComment.first.empty()) |
| continue; |
| StringRef sym = symAndComment.first.trim(); |
| if (sym.empty()) |
| continue; |
| // Check for prefix. |
| StringRef prefix; |
| std::pair<StringRef, StringRef> 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 <path> |
| // 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 <path>,<dir> |
| // In this variant, the path is to a text file which contains a partial path |
| // per line. The <dir> prefix is prepended to each partial path. |
| // |
| static std::error_code parseFileList(StringRef fileListPath, |
| std::unique_ptr<InputGraph> &inputGraph, |
| MachOLinkingContext &ctx, bool forceLoad, |
| raw_ostream &diagnostics) { |
| // If there is a comma, split off <dir>. |
| std::pair<StringRef, StringRef> opt = fileListPath.split(','); |
| StringRef filePath = opt.first; |
| StringRef dirName = opt.second; |
| ctx.addInputFileDependency(filePath); |
| // Map in file list file. |
| ErrorOr<std::unique_ptr<MemoryBuffer>> mb = |
| MemoryBuffer::getFileOrSTDIN(filePath); |
| if (std::error_code ec = mb.getError()) |
| return ec; |
| StringRef buffer = mb->get()->getBuffer(); |
| while (!buffer.empty()) { |
| // Split off each line in the file. |
| std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n'); |
| StringRef line = lineAndRest.first; |
| StringRef path; |
| if (!dirName.empty()) { |
| // If there is a <dir> 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 <dir> use whole line as input file path. |
| path = ctx.copy(line); |
| } |
| if (!ctx.pathExists(path)) { |
| return make_dynamic_error_code(Twine("File not found '") |
| + path |
| + "'"); |
| } |
| if (ctx.testingFileUsage()) { |
| diagnostics << "Found filelist entry " << canonicalizePath(path) << '\n'; |
| } |
| addFile(path, inputGraph, ctx, forceLoad, false); |
| buffer = lineAndRest.second; |
| } |
| return std::error_code(); |
| } |
| |
| /// 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); |
| } |
| |
| namespace lld { |
| |
| bool DarwinLdDriver::linkMachO(int argc, const char *argv[], |
| raw_ostream &diagnostics) { |
| MachOLinkingContext ctx; |
| if (!parse(argc, argv, ctx, diagnostics)) |
| return false; |
| if (ctx.doNothing()) |
| return true; |
| |
| // Register possible input file parsers. |
| ctx.registry().addSupportMachOObjects(ctx); |
| ctx.registry().addSupportArchives(ctx.logInputFiles()); |
| ctx.registry().addSupportNativeObjects(); |
| ctx.registry().addSupportYamlFiles(); |
| |
| return link(ctx, diagnostics); |
| } |
| |
| bool DarwinLdDriver::parse(int argc, const char *argv[], |
| MachOLinkingContext &ctx, raw_ostream &diagnostics) { |
| // Parse command line options using DarwinLdOptions.td |
| std::unique_ptr<llvm::opt::InputArgList> parsedArgs; |
| DarwinLdOptTable table; |
| unsigned missingIndex; |
| unsigned missingCount; |
| bool globalWholeArchive = false; |
| parsedArgs.reset( |
| table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); |
| if (missingCount) { |
| diagnostics << "error: missing arg value for '" |
| << parsedArgs->getArgString(missingIndex) << "' expected " |
| << missingCount << " argument(s).\n"; |
| return false; |
| } |
| |
| for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) { |
| diagnostics << "warning: ignoring unknown argument: " |
| << unknownArg->getAsString(*parsedArgs) << "\n"; |
| } |
| |
| // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static ) |
| llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE; |
| 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; |
| 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) { |
| diagnostics << "error: unknown arch named '" << archStr->getValue() |
| << "'\n"; |
| return false; |
| } |
| } |
| // If no -arch specified, scan input files to find first non-fat .o file. |
| if ((arch == MachOLinkingContext::arch_unknown) |
| && !parsedArgs->getLastArg(OPT_test_file_usage)) { |
| 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) { |
| // If no -arch and no options at all, print usage message. |
| if (parsedArgs->size() == 0) |
| table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); |
| else |
| diagnostics << "error: -arch not specified and could not be inferred\n"; |
| return false; |
| } |
| } |
| |
| // Handle -macosx_version_min or -ios_version_min |
| MachOLinkingContext::OS os = MachOLinkingContext::OS::macOSX; |
| 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)) { |
| diagnostics << "error: malformed macosx_version_min value\n"; |
| return false; |
| } |
| break; |
| case OPT_ios_version_min: |
| os = MachOLinkingContext::OS::iOS; |
| if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), |
| minOSVersion)) { |
| diagnostics << "error: malformed ios_version_min value\n"; |
| return false; |
| } |
| break; |
| case OPT_ios_simulator_version_min: |
| os = MachOLinkingContext::OS::iOS_simulator; |
| if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), |
| minOSVersion)) { |
| diagnostics << "error: malformed ios_simulator_version_min value\n"; |
| return false; |
| } |
| break; |
| } |
| } else { |
| // No min-os version on command line, check environment variables |
| } |
| |
| // Now that there's enough information parsed in, let the linking context |
| // set up default values. |
| ctx.configure(fileType, arch, os, minOSVersion); |
| |
| // 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)) { |
| diagnostics << "error: image_base expects a hex number\n"; |
| return false; |
| } else if (baseAddress < ctx.pageZeroSize()) { |
| diagnostics << "error: image_base overlaps with __PAGEZERO\n"; |
| return false; |
| } else if (baseAddress % ctx.pageSize()) { |
| diagnostics << "error: image_base must be a multiple of page size (" |
| << llvm::format("0x%" PRIx64, ctx.pageSize()) << ")\n"; |
| return false; |
| } |
| |
| ctx.setBaseAddress(baseAddress); |
| } |
| |
| // Handle -dead_strip |
| if (parsedArgs->getLastArg(OPT_dead_strip)) |
| ctx.setDeadStripping(true); |
| |
| // 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) { |
| diagnostics |
| << "error: -compatibility_version can only be used with -dylib\n"; |
| return false; |
| } |
| uint32_t parsedVers; |
| if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { |
| diagnostics << "error: -compatibility_version value is malformed\n"; |
| return false; |
| } |
| ctx.setCompatibilityVersion(parsedVers); |
| } |
| |
| if (llvm::opt::Arg *vers = parsedArgs->getLastArg(OPT_current_version)) { |
| if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { |
| diagnostics << "-current_version can only be used with -dylib\n"; |
| return false; |
| } |
| uint32_t parsedVers; |
| if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { |
| diagnostics << "error: -current_version value is malformed\n"; |
| 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)) { |
| diagnostics << "error: -sectalign alignment value '" |
| << alignStr << "' not a valid number\n"; |
| return false; |
| } |
| uint8_t align2 = llvm::countTrailingZeros(alignValue); |
| if ( (unsigned long)(1 << align2) != alignValue ) { |
| diagnostics << "warning: alignment for '-sectalign " |
| << segName << " " << sectName |
| << llvm::format(" 0x%llX", alignValue) |
| << "' is not a power of two, using " |
| << llvm::format("0x%08X", (1 << align2)) << "\n"; |
| } |
| ctx.addSectionAlignment(segName, sectName, align2); |
| } |
| |
| // 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) |
| diagnostics << "warning: -keep_private_externs only used in -r mode\n"; |
| } |
| |
| // Handle -dependency_info <path> used by Xcode. |
| if (llvm::opt::Arg *depInfo = parsedArgs->getLastArg(OPT_dependency_info)) { |
| if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) { |
| diagnostics << "warning: " << ec.message() |
| << ", processing '-dependency_info " |
| << depInfo->getValue() |
| << "'\n"; |
| } |
| } |
| |
| // 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()); |
| } |
| } |
| |
| std::unique_ptr<InputGraph> inputGraph(new InputGraph()); |
| |
| // 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<StringRef> 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 (parsedArgs->getLastArg(OPT_v)) { |
| diagnostics << "Library search paths:\n"; |
| for (auto path : ctx.searchDirs()) { |
| diagnostics << " " << path << '\n'; |
| } |
| diagnostics << "Framework search paths:\n"; |
| for (auto path : ctx.frameworkDirs()) { |
| diagnostics << " " << path << '\n'; |
| } |
| } |
| |
| // Handle -exported_symbols_list <file> |
| for (auto expFile : parsedArgs->filtered(OPT_exported_symbols_list)) { |
| if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { |
| diagnostics << "error: -exported_symbols_list cannot be combined " |
| << "with -unexported_symbol[s_list]\n"; |
| return false; |
| } |
| ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); |
| if (std::error_code ec = parseExportsList(expFile->getValue(), ctx, |
| diagnostics)) { |
| diagnostics << "error: " << ec.message() |
| << ", processing '-exported_symbols_list " |
| << expFile->getValue() |
| << "'\n"; |
| return false; |
| } |
| } |
| |
| // Handle -exported_symbol <symbol> |
| for (auto symbol : parsedArgs->filtered(OPT_exported_symbol)) { |
| if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { |
| diagnostics << "error: -exported_symbol cannot be combined " |
| << "with -unexported_symbol[s_list]\n"; |
| return false; |
| } |
| ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); |
| ctx.addExportSymbol(symbol->getValue()); |
| } |
| |
| // Handle -unexported_symbols_list <file> |
| for (auto expFile : parsedArgs->filtered(OPT_unexported_symbols_list)) { |
| if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { |
| diagnostics << "error: -unexported_symbols_list cannot be combined " |
| << "with -exported_symbol[s_list]\n"; |
| return false; |
| } |
| ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); |
| if (std::error_code ec = parseExportsList(expFile->getValue(), ctx, |
| diagnostics)) { |
| diagnostics << "error: " << ec.message() |
| << ", processing '-unexported_symbols_list " |
| << expFile->getValue() |
| << "'\n"; |
| return false; |
| } |
| } |
| |
| // Handle -unexported_symbol <symbol> |
| for (auto symbol : parsedArgs->filtered(OPT_unexported_symbol)) { |
| if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { |
| diagnostics << "error: -unexported_symbol cannot be combined " |
| << "with -exported_symbol[s_list]\n"; |
| return false; |
| } |
| ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); |
| 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) { |
| diagnostics << "warning: -multi_module is obsolete and being ignored\n"; |
| } |
| else { |
| if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { |
| diagnostics << "warning: -single_module being ignored. " |
| "It is only for use when producing a dylib\n"; |
| } |
| } |
| } |
| |
| // 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)) { |
| diagnostics << "-pie can only be used when targeting " |
| "Mac OS X 10.5 or later\n"; |
| return false; |
| } |
| break; |
| case MachOLinkingContext::OS::iOS: |
| if ((minOSVersion < 0x00040200) && |
| (pie->getOption().getID() == OPT_pie)) { |
| diagnostics << "-pie can only be used when targeting " |
| "iOS 4.2 or later\n"; |
| return false; |
| } |
| break; |
| case MachOLinkingContext::OS::iOS_simulator: |
| if (pie->getOption().getID() == OPT_no_pie) |
| diagnostics << "iOS simulator programs must be built PIE\n"; |
| 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: |
| diagnostics << "warning: " << pie->getSpelling() << " being ignored. " |
| << "It is only used when linking main executables\n"; |
| break; |
| default: |
| diagnostics << pie->getSpelling() |
| << " can only used when linking main executables\n"; |
| return false; |
| break; |
| } |
| } |
| |
| // Handle debug info handling options: -S |
| if (parsedArgs->hasArg(OPT_S)) |
| ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap); |
| |
| // Handle -order_file <file> |
| for (auto orderFile : parsedArgs->filtered(OPT_order_file)) { |
| if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx, |
| diagnostics)) { |
| diagnostics << "error: " << ec.message() |
| << ", processing '-order_file " |
| << orderFile->getValue() |
| << "'\n"; |
| return false; |
| } |
| } |
| |
| // Handle -rpath <path> |
| 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) { |
| diagnostics << "error: -rpath can only be used when targeting " |
| "OS X 10.5 or later\n"; |
| } else { |
| diagnostics << "error: -rpath can only be used when targeting " |
| "iOS 2.0 or later\n"; |
| } |
| return false; |
| } |
| break; |
| default: |
| diagnostics << "error: -rpath can only be used when creating " |
| "a dynamic final linked image\n"; |
| return false; |
| } |
| |
| for (auto rPath : parsedArgs->filtered(OPT_rpath)) { |
| ctx.addRpath(rPath->getValue()); |
| } |
| } |
| |
| // Handle input files |
| for (auto &arg : *parsedArgs) { |
| bool upward; |
| ErrorOr<StringRef> resolvedPath = StringRef(); |
| switch (arg->getOption().getID()) { |
| default: |
| continue; |
| case OPT_INPUT: |
| addFile(arg->getValue(), inputGraph, ctx, globalWholeArchive, false); |
| break; |
| case OPT_upward_library: |
| addFile(arg->getValue(), inputGraph, ctx, false, true); |
| break; |
| case OPT_force_load: |
| addFile(arg->getValue(), inputGraph, 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) { |
| diagnostics << "Unable to find library for " << arg->getSpelling() |
| << arg->getValue() << "\n"; |
| return false; |
| } else if (ctx.testingFileUsage()) { |
| diagnostics << "Found " << (upward ? "upward " : " ") << "library " |
| << canonicalizePath(resolvedPath.get()) << '\n'; |
| } |
| addFile(resolvedPath.get(), inputGraph, 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) { |
| diagnostics << "Unable to find framework for " |
| << arg->getSpelling() << " " << arg->getValue() << "\n"; |
| return false; |
| } else if (ctx.testingFileUsage()) { |
| diagnostics << "Found " << (upward ? "upward " : " ") << "framework " |
| << canonicalizePath(resolvedPath.get()) << '\n'; |
| } |
| addFile(resolvedPath.get(), inputGraph, ctx, globalWholeArchive, upward); |
| break; |
| case OPT_filelist: |
| if (std::error_code ec = parseFileList(arg->getValue(), inputGraph, |
| ctx, globalWholeArchive, |
| diagnostics)) { |
| diagnostics << "error: " << ec.message() |
| << ", processing '-filelist " << arg->getValue() |
| << "'\n"; |
| return false; |
| } |
| break; |
| } |
| } |
| |
| if (!inputGraph->size()) { |
| diagnostics << "No input files\n"; |
| return false; |
| } |
| |
| ctx.setInputGraph(std::move(inputGraph)); |
| |
| // Validate the combination of options used. |
| return ctx.validate(diagnostics); |
| } |
| |
| |
| } // namespace lld |