void LinkerDriver::linkerMain()

in lld/COFF/Driver.cpp [1252:2281]


void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
  ScopedTimer rootTimer(ctx.rootTimer);

  // Needed for LTO.
  InitializeAllTargetInfos();
  InitializeAllTargets();
  InitializeAllTargetMCs();
  InitializeAllAsmParsers();
  InitializeAllAsmPrinters();

  // If the first command line argument is "/lib", link.exe acts like lib.exe.
  // We call our own implementation of lib.exe that understands bitcode files.
  if (argsArr.size() > 1 &&
      (StringRef(argsArr[1]).equals_insensitive("/lib") ||
       StringRef(argsArr[1]).equals_insensitive("-lib"))) {
    if (llvm::libDriverMain(argsArr.slice(1)) != 0)
      fatal("lib failed");
    return;
  }

  // Parse command line options.
  ArgParser parser;
  opt::InputArgList args = parser.parse(argsArr);

  // Parse and evaluate -mllvm options.
  std::vector<const char *> v;
  v.push_back("lld-link (LLVM option parsing)");
  for (auto *arg : args.filtered(OPT_mllvm))
    v.push_back(arg->getValue());
  cl::ResetAllOptionOccurrences();
  cl::ParseCommandLineOptions(v.size(), v.data());

  // Handle /errorlimit early, because error() depends on it.
  if (auto *arg = args.getLastArg(OPT_errorlimit)) {
    int n = 20;
    StringRef s = arg->getValue();
    if (s.getAsInteger(10, n))
      error(arg->getSpelling() + " number expected, but got " + s);
    errorHandler().errorLimit = n;
  }

  // Handle /help
  if (args.hasArg(OPT_help)) {
    printHelp(argsArr[0]);
    return;
  }

  // /threads: takes a positive integer and provides the default value for
  // /opt:lldltojobs=.
  if (auto *arg = args.getLastArg(OPT_threads)) {
    StringRef v(arg->getValue());
    unsigned threads = 0;
    if (!llvm::to_integer(v, threads, 0) || threads == 0)
      error(arg->getSpelling() + ": expected a positive integer, but got '" +
            arg->getValue() + "'");
    parallel::strategy = hardware_concurrency(threads);
    config->thinLTOJobs = v.str();
  }

  if (args.hasArg(OPT_show_timing))
    config->showTiming = true;

  config->showSummary = args.hasArg(OPT_summary);

  // Handle --version, which is an lld extension. This option is a bit odd
  // because it doesn't start with "/", but we deliberately chose "--" to
  // avoid conflict with /version and for compatibility with clang-cl.
  if (args.hasArg(OPT_dash_dash_version)) {
    message(getLLDVersion());
    return;
  }

  // Handle /lldmingw early, since it can potentially affect how other
  // options are handled.
  config->mingw = args.hasArg(OPT_lldmingw);

  // Handle /linkrepro and /reproduce.
  if (Optional<std::string> path = getReproduceFile(args)) {
    Expected<std::unique_ptr<TarWriter>> errOrWriter =
        TarWriter::create(*path, sys::path::stem(*path));

    if (errOrWriter) {
      tar = std::move(*errOrWriter);
    } else {
      error("/linkrepro: failed to open " + *path + ": " +
            toString(errOrWriter.takeError()));
    }
  }

  if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) {
    if (args.hasArg(OPT_deffile))
      config->noEntry = true;
    else
      fatal("no input files");
  }

  // Construct search path list.
  searchPaths.push_back("");
  for (auto *arg : args.filtered(OPT_libpath))
    searchPaths.push_back(arg->getValue());
  if (!args.hasArg(OPT_lldignoreenv))
    addLibSearchPaths();

  // Handle /ignore
  for (auto *arg : args.filtered(OPT_ignore)) {
    SmallVector<StringRef, 8> vec;
    StringRef(arg->getValue()).split(vec, ',');
    for (StringRef s : vec) {
      if (s == "4037")
        config->warnMissingOrderSymbol = false;
      else if (s == "4099")
        config->warnDebugInfoUnusable = false;
      else if (s == "4217")
        config->warnLocallyDefinedImported = false;
      else if (s == "longsections")
        config->warnLongSectionNames = false;
      // Other warning numbers are ignored.
    }
  }

  // Handle /out
  if (auto *arg = args.getLastArg(OPT_out))
    config->outputFile = arg->getValue();

  // Handle /verbose
  if (args.hasArg(OPT_verbose))
    config->verbose = true;
  errorHandler().verbose = config->verbose;

  // Handle /force or /force:unresolved
  if (args.hasArg(OPT_force, OPT_force_unresolved))
    config->forceUnresolved = true;

  // Handle /force or /force:multiple
  if (args.hasArg(OPT_force, OPT_force_multiple))
    config->forceMultiple = true;

  // Handle /force or /force:multipleres
  if (args.hasArg(OPT_force, OPT_force_multipleres))
    config->forceMultipleRes = true;

  // Handle /debug
  DebugKind debug = parseDebugKind(args);
  if (debug == DebugKind::Full || debug == DebugKind::Dwarf ||
      debug == DebugKind::GHash || debug == DebugKind::NoGHash) {
    config->debug = true;
    config->incremental = true;
  }

  // Handle /demangle
  config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no);

  // Handle /debugtype
  config->debugTypes = parseDebugTypes(args);

  // Handle /driver[:uponly|:wdm].
  config->driverUponly = args.hasArg(OPT_driver_uponly) ||
                         args.hasArg(OPT_driver_uponly_wdm) ||
                         args.hasArg(OPT_driver_wdm_uponly);
  config->driverWdm = args.hasArg(OPT_driver_wdm) ||
                      args.hasArg(OPT_driver_uponly_wdm) ||
                      args.hasArg(OPT_driver_wdm_uponly);
  config->driver =
      config->driverUponly || config->driverWdm || args.hasArg(OPT_driver);

  // Handle /pdb
  bool shouldCreatePDB =
      (debug == DebugKind::Full || debug == DebugKind::GHash ||
       debug == DebugKind::NoGHash);
  if (shouldCreatePDB) {
    if (auto *arg = args.getLastArg(OPT_pdb))
      config->pdbPath = arg->getValue();
    if (auto *arg = args.getLastArg(OPT_pdbaltpath))
      config->pdbAltPath = arg->getValue();
    if (auto *arg = args.getLastArg(OPT_pdbpagesize))
      parsePDBPageSize(arg->getValue());
    if (args.hasArg(OPT_natvis))
      config->natvisFiles = args.getAllArgValues(OPT_natvis);
    if (args.hasArg(OPT_pdbstream)) {
      for (const StringRef value : args.getAllArgValues(OPT_pdbstream)) {
        const std::pair<StringRef, StringRef> nameFile = value.split("=");
        const StringRef name = nameFile.first;
        const std::string file = nameFile.second.str();
        config->namedStreams[name] = file;
      }
    }

    if (auto *arg = args.getLastArg(OPT_pdb_source_path))
      config->pdbSourcePath = arg->getValue();
  }

  // Handle /pdbstripped
  if (args.hasArg(OPT_pdbstripped))
    warn("ignoring /pdbstripped flag, it is not yet supported");

  // Handle /noentry
  if (args.hasArg(OPT_noentry)) {
    if (args.hasArg(OPT_dll))
      config->noEntry = true;
    else
      error("/noentry must be specified with /dll");
  }

  // Handle /dll
  if (args.hasArg(OPT_dll)) {
    config->dll = true;
    config->manifestID = 2;
  }

  // Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase
  // because we need to explicitly check whether that option or its inverse was
  // present in the argument list in order to handle /fixed.
  auto *dynamicBaseArg = args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no);
  if (dynamicBaseArg &&
      dynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
    config->dynamicBase = false;

  // MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the
  // default setting for any other project type.", but link.exe defaults to
  // /FIXED:NO for exe outputs as well. Match behavior, not docs.
  bool fixed = args.hasFlag(OPT_fixed, OPT_fixed_no, false);
  if (fixed) {
    if (dynamicBaseArg &&
        dynamicBaseArg->getOption().getID() == OPT_dynamicbase) {
      error("/fixed must not be specified with /dynamicbase");
    } else {
      config->relocatable = false;
      config->dynamicBase = false;
    }
  }

  // Handle /appcontainer
  config->appContainer =
      args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false);

  // Handle /machine
  if (auto *arg = args.getLastArg(OPT_machine)) {
    config->machine = getMachineType(arg->getValue());
    if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN)
      fatal(Twine("unknown /machine argument: ") + arg->getValue());
  }

  // Handle /nodefaultlib:<filename>
  for (auto *arg : args.filtered(OPT_nodefaultlib))
    config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower());

  // Handle /nodefaultlib
  if (args.hasArg(OPT_nodefaultlib_all))
    config->noDefaultLibAll = true;

  // Handle /base
  if (auto *arg = args.getLastArg(OPT_base))
    parseNumbers(arg->getValue(), &config->imageBase);

  // Handle /filealign
  if (auto *arg = args.getLastArg(OPT_filealign)) {
    parseNumbers(arg->getValue(), &config->fileAlign);
    if (!isPowerOf2_64(config->fileAlign))
      error("/filealign: not a power of two: " + Twine(config->fileAlign));
  }

  // Handle /stack
  if (auto *arg = args.getLastArg(OPT_stack))
    parseNumbers(arg->getValue(), &config->stackReserve, &config->stackCommit);

  // Handle /guard:cf
  if (auto *arg = args.getLastArg(OPT_guard))
    parseGuard(arg->getValue());

  // Handle /heap
  if (auto *arg = args.getLastArg(OPT_heap))
    parseNumbers(arg->getValue(), &config->heapReserve, &config->heapCommit);

  // Handle /version
  if (auto *arg = args.getLastArg(OPT_version))
    parseVersion(arg->getValue(), &config->majorImageVersion,
                 &config->minorImageVersion);

  // Handle /subsystem
  if (auto *arg = args.getLastArg(OPT_subsystem))
    parseSubsystem(arg->getValue(), &config->subsystem,
                   &config->majorSubsystemVersion,
                   &config->minorSubsystemVersion);

  // Handle /osversion
  if (auto *arg = args.getLastArg(OPT_osversion)) {
    parseVersion(arg->getValue(), &config->majorOSVersion,
                 &config->minorOSVersion);
  } else {
    config->majorOSVersion = config->majorSubsystemVersion;
    config->minorOSVersion = config->minorSubsystemVersion;
  }

  // Handle /timestamp
  if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) {
    if (arg->getOption().getID() == OPT_repro) {
      config->timestamp = 0;
      config->repro = true;
    } else {
      config->repro = false;
      StringRef value(arg->getValue());
      if (value.getAsInteger(0, config->timestamp))
        fatal(Twine("invalid timestamp: ") + value +
              ".  Expected 32-bit integer");
    }
  } else {
    config->repro = false;
    config->timestamp = time(nullptr);
  }

  // Handle /alternatename
  for (auto *arg : args.filtered(OPT_alternatename))
    parseAlternateName(arg->getValue());

  // Handle /include
  for (auto *arg : args.filtered(OPT_incl))
    addUndefined(arg->getValue());

  // Handle /implib
  if (auto *arg = args.getLastArg(OPT_implib))
    config->implib = arg->getValue();

  // Handle /opt.
  bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile);
  Optional<ICFLevel> icfLevel = None;
  if (args.hasArg(OPT_profile))
    icfLevel = ICFLevel::None;
  unsigned tailMerge = 1;
  bool ltoNewPM = LLVM_ENABLE_NEW_PASS_MANAGER;
  bool ltoDebugPM = false;
  for (auto *arg : args.filtered(OPT_opt)) {
    std::string str = StringRef(arg->getValue()).lower();
    SmallVector<StringRef, 1> vec;
    StringRef(str).split(vec, ',');
    for (StringRef s : vec) {
      if (s == "ref") {
        doGC = true;
      } else if (s == "noref") {
        doGC = false;
      } else if (s == "icf" || s.startswith("icf=")) {
        icfLevel = ICFLevel::All;
      } else if (s == "safeicf") {
        icfLevel = ICFLevel::Safe;
      } else if (s == "noicf") {
        icfLevel = ICFLevel::None;
      } else if (s == "lldtailmerge") {
        tailMerge = 2;
      } else if (s == "nolldtailmerge") {
        tailMerge = 0;
      } else if (s == "ltonewpassmanager") {
        ltoNewPM = true;
      } else if (s == "noltonewpassmanager") {
        ltoNewPM = false;
      } else if (s == "ltodebugpassmanager") {
        ltoDebugPM = true;
      } else if (s == "noltodebugpassmanager") {
        ltoDebugPM = false;
      } else if (s.startswith("lldlto=")) {
        StringRef optLevel = s.substr(7);
        if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3)
          error("/opt:lldlto: invalid optimization level: " + optLevel);
      } else if (s.startswith("lldltojobs=")) {
        StringRef jobs = s.substr(11);
        if (!get_threadpool_strategy(jobs))
          error("/opt:lldltojobs: invalid job count: " + jobs);
        config->thinLTOJobs = jobs.str();
      } else if (s.startswith("lldltopartitions=")) {
        StringRef n = s.substr(17);
        if (n.getAsInteger(10, config->ltoPartitions) ||
            config->ltoPartitions == 0)
          error("/opt:lldltopartitions: invalid partition count: " + n);
      } else if (s != "lbr" && s != "nolbr")
        error("/opt: unknown option: " + s);
    }
  }

  if (!icfLevel)
    icfLevel = doGC ? ICFLevel::All : ICFLevel::None;
  config->doGC = doGC;
  config->doICF = icfLevel.getValue();
  config->tailMerge =
      (tailMerge == 1 && config->doICF != ICFLevel::None) || tailMerge == 2;
  config->ltoNewPassManager = ltoNewPM;
  config->ltoDebugPassManager = ltoDebugPM;

  // Handle /lldsavetemps
  if (args.hasArg(OPT_lldsavetemps))
    config->saveTemps = true;

  // Handle /kill-at
  if (args.hasArg(OPT_kill_at))
    config->killAt = true;

  // Handle /lldltocache
  if (auto *arg = args.getLastArg(OPT_lldltocache))
    config->ltoCache = arg->getValue();

  // Handle /lldsavecachepolicy
  if (auto *arg = args.getLastArg(OPT_lldltocachepolicy))
    config->ltoCachePolicy = CHECK(
        parseCachePruningPolicy(arg->getValue()),
        Twine("/lldltocachepolicy: invalid cache policy: ") + arg->getValue());

  // Handle /failifmismatch
  for (auto *arg : args.filtered(OPT_failifmismatch))
    checkFailIfMismatch(arg->getValue(), nullptr);

  // Handle /merge
  for (auto *arg : args.filtered(OPT_merge))
    parseMerge(arg->getValue());

  // Add default section merging rules after user rules. User rules take
  // precedence, but we will emit a warning if there is a conflict.
  parseMerge(".idata=.rdata");
  parseMerge(".didat=.rdata");
  parseMerge(".edata=.rdata");
  parseMerge(".xdata=.rdata");
  parseMerge(".bss=.data");

  if (config->mingw) {
    parseMerge(".ctors=.rdata");
    parseMerge(".dtors=.rdata");
    parseMerge(".CRT=.rdata");
  }

  // Handle /section
  for (auto *arg : args.filtered(OPT_section))
    parseSection(arg->getValue());

  // Handle /align
  if (auto *arg = args.getLastArg(OPT_align)) {
    parseNumbers(arg->getValue(), &config->align);
    if (!isPowerOf2_64(config->align))
      error("/align: not a power of two: " + StringRef(arg->getValue()));
    if (!args.hasArg(OPT_driver))
      warn("/align specified without /driver; image may not run");
  }

  // Handle /aligncomm
  for (auto *arg : args.filtered(OPT_aligncomm))
    parseAligncomm(arg->getValue());

  // Handle /manifestdependency.
  for (auto *arg : args.filtered(OPT_manifestdependency))
    config->manifestDependencies.insert(arg->getValue());

  // Handle /manifest and /manifest:
  if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) {
    if (arg->getOption().getID() == OPT_manifest)
      config->manifest = Configuration::SideBySide;
    else
      parseManifest(arg->getValue());
  }

  // Handle /manifestuac
  if (auto *arg = args.getLastArg(OPT_manifestuac))
    parseManifestUAC(arg->getValue());

  // Handle /manifestfile
  if (auto *arg = args.getLastArg(OPT_manifestfile))
    config->manifestFile = arg->getValue();

  // Handle /manifestinput
  for (auto *arg : args.filtered(OPT_manifestinput))
    config->manifestInput.push_back(arg->getValue());

  if (!config->manifestInput.empty() &&
      config->manifest != Configuration::Embed) {
    fatal("/manifestinput: requires /manifest:embed");
  }

  config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
  config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
                             args.hasArg(OPT_thinlto_index_only_arg);
  config->thinLTOIndexOnlyArg =
      args.getLastArgValue(OPT_thinlto_index_only_arg);
  config->thinLTOPrefixReplace =
      getOldNewOptions(args, OPT_thinlto_prefix_replace);
  config->thinLTOObjectSuffixReplace =
      getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
  config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path);
  config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate);
  config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file);
  // Handle miscellaneous boolean flags.
  config->ltoPGOWarnMismatch = args.hasFlag(OPT_lto_pgo_warn_mismatch,
                                            OPT_lto_pgo_warn_mismatch_no, true);
  config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
  config->allowIsolation =
      args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
  config->incremental =
      args.hasFlag(OPT_incremental, OPT_incremental_no,
                   !config->doGC && config->doICF == ICFLevel::None &&
                       !args.hasArg(OPT_order) && !args.hasArg(OPT_profile));
  config->integrityCheck =
      args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
  config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
  config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
  for (auto *arg : args.filtered(OPT_swaprun))
    parseSwaprun(arg->getValue());
  config->terminalServerAware =
      !config->dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
  config->debugDwarf = debug == DebugKind::Dwarf;
  config->debugGHashes = debug == DebugKind::GHash || debug == DebugKind::Full;
  config->debugSymtab = debug == DebugKind::Symtab;
  config->autoImport =
      args.hasFlag(OPT_auto_import, OPT_auto_import_no, config->mingw);
  config->pseudoRelocs = args.hasFlag(
      OPT_runtime_pseudo_reloc, OPT_runtime_pseudo_reloc_no, config->mingw);
  config->callGraphProfileSort = args.hasFlag(
      OPT_call_graph_profile_sort, OPT_call_graph_profile_sort_no, true);
  config->stdcallFixup =
      args.hasFlag(OPT_stdcall_fixup, OPT_stdcall_fixup_no, config->mingw);
  config->warnStdcallFixup = !args.hasArg(OPT_stdcall_fixup);

  // Don't warn about long section names, such as .debug_info, for mingw or
  // when -debug:dwarf is requested.
  if (config->mingw || config->debugDwarf)
    config->warnLongSectionNames = false;

  config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file);
  config->mapFile = getMapFile(args, OPT_map, OPT_map_file);

  if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) {
    warn("/lldmap and /map have the same output file '" + config->mapFile +
         "'.\n>>> ignoring /lldmap");
    config->lldmapFile.clear();
  }

  if (config->incremental && args.hasArg(OPT_profile)) {
    warn("ignoring '/incremental' due to '/profile' specification");
    config->incremental = false;
  }

  if (config->incremental && args.hasArg(OPT_order)) {
    warn("ignoring '/incremental' due to '/order' specification");
    config->incremental = false;
  }

  if (config->incremental && config->doGC) {
    warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to "
         "disable");
    config->incremental = false;
  }

  if (config->incremental && config->doICF != ICFLevel::None) {
    warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to "
         "disable");
    config->incremental = false;
  }

  if (errorCount())
    return;

  std::set<sys::fs::UniqueID> wholeArchives;
  for (auto *arg : args.filtered(OPT_wholearchive_file))
    if (Optional<StringRef> path = doFindFile(arg->getValue()))
      if (Optional<sys::fs::UniqueID> id = getUniqueID(*path))
        wholeArchives.insert(*id);

  // A predicate returning true if a given path is an argument for
  // /wholearchive:, or /wholearchive is enabled globally.
  // This function is a bit tricky because "foo.obj /wholearchive:././foo.obj"
  // needs to be handled as "/wholearchive:foo.obj foo.obj".
  auto isWholeArchive = [&](StringRef path) -> bool {
    if (args.hasArg(OPT_wholearchive_flag))
      return true;
    if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
      return wholeArchives.count(*id);
    return false;
  };

  // Create a list of input files. These can be given as OPT_INPUT options
  // and OPT_wholearchive_file options, and we also need to track OPT_start_lib
  // and OPT_end_lib.
  bool inLib = false;
  for (auto *arg : args) {
    switch (arg->getOption().getID()) {
    case OPT_end_lib:
      if (!inLib)
        error("stray " + arg->getSpelling());
      inLib = false;
      break;
    case OPT_start_lib:
      if (inLib)
        error("nested " + arg->getSpelling());
      inLib = true;
      break;
    case OPT_wholearchive_file:
      if (Optional<StringRef> path = findFile(arg->getValue()))
        enqueuePath(*path, true, inLib);
      break;
    case OPT_INPUT:
      if (Optional<StringRef> path = findFile(arg->getValue()))
        enqueuePath(*path, isWholeArchive(*path), inLib);
      break;
    default:
      // Ignore other options.
      break;
    }
  }

  // Process files specified as /defaultlib. These should be enequeued after
  // other files, which is why they are in a separate loop.
  for (auto *arg : args.filtered(OPT_defaultlib))
    if (Optional<StringRef> path = findLib(arg->getValue()))
      enqueuePath(*path, false, false);

  // Read all input files given via the command line.
  run();

  if (errorCount())
    return;

  // We should have inferred a machine type by now from the input files, but if
  // not we assume x64.
  if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
    warn("/machine is not specified. x64 is assumed");
    config->machine = AMD64;
  }
  config->wordsize = config->is64() ? 8 : 4;

  // Handle /safeseh, x86 only, on by default, except for mingw.
  if (config->machine == I386) {
    config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw);
    config->noSEH = args.hasArg(OPT_noseh);
  }

  // Handle /functionpadmin
  for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
    parseFunctionPadMin(arg, config->machine);

  if (tar)
    tar->append("response.txt",
                createResponseFile(args, filePaths,
                                   ArrayRef<StringRef>(searchPaths).slice(1)));

  // Handle /largeaddressaware
  config->largeAddressAware = args.hasFlag(
      OPT_largeaddressaware, OPT_largeaddressaware_no, config->is64());

  // Handle /highentropyva
  config->highEntropyVA =
      config->is64() &&
      args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true);

  if (!config->dynamicBase &&
      (config->machine == ARMNT || config->machine == ARM64))
    error("/dynamicbase:no is not compatible with " +
          machineToStr(config->machine));

  // Handle /export
  for (auto *arg : args.filtered(OPT_export)) {
    Export e = parseExport(arg->getValue());
    if (config->machine == I386) {
      if (!isDecorated(e.name))
        e.name = saver.save("_" + e.name);
      if (!e.extName.empty() && !isDecorated(e.extName))
        e.extName = saver.save("_" + e.extName);
    }
    config->exports.push_back(e);
  }

  // Handle /def
  if (auto *arg = args.getLastArg(OPT_deffile)) {
    // parseModuleDefs mutates Config object.
    parseModuleDefs(arg->getValue());
  }

  // Handle generation of import library from a def file.
  if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) {
    fixupExports();
    createImportLibrary(/*asLib=*/true);
    return;
  }

  // Windows specific -- if no /subsystem is given, we need to infer
  // that from entry point name.  Must happen before /entry handling,
  // and after the early return when just writing an import library.
  if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
    config->subsystem = inferSubsystem();
    if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
      fatal("subsystem must be defined");
  }

  // Handle /entry and /dll
  if (auto *arg = args.getLastArg(OPT_entry)) {
    config->entry = addUndefined(mangle(arg->getValue()));
  } else if (!config->entry && !config->noEntry) {
    if (args.hasArg(OPT_dll)) {
      StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12"
                                              : "_DllMainCRTStartup";
      config->entry = addUndefined(s);
    } else if (config->driverWdm) {
      // /driver:wdm implies /entry:_NtProcessStartup
      config->entry = addUndefined(mangle("_NtProcessStartup"));
    } else {
      // Windows specific -- If entry point name is not given, we need to
      // infer that from user-defined entry name.
      StringRef s = findDefaultEntry();
      if (s.empty())
        fatal("entry point must be defined");
      config->entry = addUndefined(s);
      log("Entry name inferred: " + s);
    }
  }

  // Handle /delayload
  for (auto *arg : args.filtered(OPT_delayload)) {
    config->delayLoads.insert(StringRef(arg->getValue()).lower());
    if (config->machine == I386) {
      config->delayLoadHelper = addUndefined("___delayLoadHelper2@8");
    } else {
      config->delayLoadHelper = addUndefined("__delayLoadHelper2");
    }
  }

  // Set default image name if neither /out or /def set it.
  if (config->outputFile.empty()) {
    config->outputFile = getOutputPath(
        (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue());
  }

  // Fail early if an output file is not writable.
  if (auto e = tryCreateFile(config->outputFile)) {
    error("cannot open output file " + config->outputFile + ": " + e.message());
    return;
  }

  if (shouldCreatePDB) {
    // Put the PDB next to the image if no /pdb flag was passed.
    if (config->pdbPath.empty()) {
      config->pdbPath = config->outputFile;
      sys::path::replace_extension(config->pdbPath, ".pdb");
    }

    // The embedded PDB path should be the absolute path to the PDB if no
    // /pdbaltpath flag was passed.
    if (config->pdbAltPath.empty()) {
      config->pdbAltPath = config->pdbPath;

      // It's important to make the path absolute and remove dots.  This path
      // will eventually be written into the PE header, and certain Microsoft
      // tools won't work correctly if these assumptions are not held.
      sys::fs::make_absolute(config->pdbAltPath);
      sys::path::remove_dots(config->pdbAltPath);
    } else {
      // Don't do this earlier, so that Config->OutputFile is ready.
      parsePDBAltPath(config->pdbAltPath);
    }
  }

  // Set default image base if /base is not given.
  if (config->imageBase == uint64_t(-1))
    config->imageBase = getDefaultImageBase();

  ctx.symtab.addSynthetic(mangle("__ImageBase"), nullptr);
  if (config->machine == I386) {
    ctx.symtab.addAbsolute("___safe_se_handler_table", 0);
    ctx.symtab.addAbsolute("___safe_se_handler_count", 0);
  }

  ctx.symtab.addAbsolute(mangle("__guard_fids_count"), 0);
  ctx.symtab.addAbsolute(mangle("__guard_fids_table"), 0);
  ctx.symtab.addAbsolute(mangle("__guard_flags"), 0);
  ctx.symtab.addAbsolute(mangle("__guard_iat_count"), 0);
  ctx.symtab.addAbsolute(mangle("__guard_iat_table"), 0);
  ctx.symtab.addAbsolute(mangle("__guard_longjmp_count"), 0);
  ctx.symtab.addAbsolute(mangle("__guard_longjmp_table"), 0);
  // Needed for MSVC 2017 15.5 CRT.
  ctx.symtab.addAbsolute(mangle("__enclave_config"), 0);
  // Needed for MSVC 2019 16.8 CRT.
  ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0);
  ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);

  if (config->pseudoRelocs) {
    ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
    ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
  }
  if (config->mingw) {
    ctx.symtab.addAbsolute(mangle("__CTOR_LIST__"), 0);
    ctx.symtab.addAbsolute(mangle("__DTOR_LIST__"), 0);
  }

  // This code may add new undefined symbols to the link, which may enqueue more
  // symbol resolution tasks, so we need to continue executing tasks until we
  // converge.
  do {
    // Windows specific -- if entry point is not found,
    // search for its mangled names.
    if (config->entry)
      mangleMaybe(config->entry);

    // Windows specific -- Make sure we resolve all dllexported symbols.
    for (Export &e : config->exports) {
      if (!e.forwardTo.empty())
        continue;
      e.sym = addUndefined(e.name);
      if (!e.directives)
        e.symbolName = mangleMaybe(e.sym);
    }

    // Add weak aliases. Weak aliases is a mechanism to give remaining
    // undefined symbols final chance to be resolved successfully.
    for (auto pair : config->alternateNames) {
      StringRef from = pair.first;
      StringRef to = pair.second;
      Symbol *sym = ctx.symtab.find(from);
      if (!sym)
        continue;
      if (auto *u = dyn_cast<Undefined>(sym))
        if (!u->weakAlias)
          u->weakAlias = ctx.symtab.addUndefined(to);
    }

    // If any inputs are bitcode files, the LTO code generator may create
    // references to library functions that are not explicit in the bitcode
    // file's symbol table. If any of those library functions are defined in a
    // bitcode file in an archive member, we need to arrange to use LTO to
    // compile those archive members by adding them to the link beforehand.
    if (!ctx.bitcodeFileInstances.empty())
      for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
        ctx.symtab.addLibcall(s);

    // Windows specific -- if __load_config_used can be resolved, resolve it.
    if (ctx.symtab.findUnderscore("_load_config_used"))
      addUndefined(mangle("_load_config_used"));
  } while (run());

  if (args.hasArg(OPT_include_optional)) {
    // Handle /includeoptional
    for (auto *arg : args.filtered(OPT_include_optional))
      if (isa_and_nonnull<LazyArchive>(ctx.symtab.find(arg->getValue())))
        addUndefined(arg->getValue());
    while (run());
  }

  // Create wrapped symbols for -wrap option.
  std::vector<WrappedSymbol> wrapped = addWrappedSymbols(ctx, args);
  // Load more object files that might be needed for wrapped symbols.
  if (!wrapped.empty())
    while (run());

  if (config->autoImport || config->stdcallFixup) {
    // MinGW specific.
    // Load any further object files that might be needed for doing automatic
    // imports, and do stdcall fixups.
    //
    // For cases with no automatically imported symbols, this iterates once
    // over the symbol table and doesn't do anything.
    //
    // For the normal case with a few automatically imported symbols, this
    // should only need to be run once, since each new object file imported
    // is an import library and wouldn't add any new undefined references,
    // but there's nothing stopping the __imp_ symbols from coming from a
    // normal object file as well (although that won't be used for the
    // actual autoimport later on). If this pass adds new undefined references,
    // we won't iterate further to resolve them.
    //
    // If stdcall fixups only are needed for loading import entries from
    // a DLL without import library, this also just needs running once.
    // If it ends up pulling in more object files from static libraries,
    // (and maybe doing more stdcall fixups along the way), this would need
    // to loop these two calls.
    ctx.symtab.loadMinGWSymbols();
    run();
  }

  // At this point, we should not have any symbols that cannot be resolved.
  // If we are going to do codegen for link-time optimization, check for
  // unresolvable symbols first, so we don't spend time generating code that
  // will fail to link anyway.
  if (!ctx.bitcodeFileInstances.empty() && !config->forceUnresolved)
    ctx.symtab.reportUnresolvable();
  if (errorCount())
    return;

  config->hadExplicitExports = !config->exports.empty();
  if (config->mingw) {
    // In MinGW, all symbols are automatically exported if no symbols
    // are chosen to be exported.
    maybeExportMinGWSymbols(args);
  }

  // Do LTO by compiling bitcode input files to a set of native COFF files then
  // link those files (unless -thinlto-index-only was given, in which case we
  // resolve symbols and write indices, but don't generate native code or link).
  ctx.symtab.compileBitcodeFiles();

  // If -thinlto-index-only is given, we should create only "index
  // files" and not object files. Index file creation is already done
  // in compileBitcodeFiles, so we are done if that's the case.
  if (config->thinLTOIndexOnly)
    return;

  // If we generated native object files from bitcode files, this resolves
  // references to the symbols we use from them.
  run();

  // Apply symbol renames for -wrap.
  if (!wrapped.empty())
    wrapSymbols(ctx, wrapped);

  // Resolve remaining undefined symbols and warn about imported locals.
  ctx.symtab.resolveRemainingUndefines();
  if (errorCount())
    return;

  if (config->mingw) {
    // Make sure the crtend.o object is the last object file. This object
    // file can contain terminating section chunks that need to be placed
    // last. GNU ld processes files and static libraries explicitly in the
    // order provided on the command line, while lld will pull in needed
    // files from static libraries only after the last object file on the
    // command line.
    for (auto i = ctx.objFileInstances.begin(), e = ctx.objFileInstances.end();
         i != e; i++) {
      ObjFile *file = *i;
      if (isCrtend(file->getName())) {
        ctx.objFileInstances.erase(i);
        ctx.objFileInstances.push_back(file);
        break;
      }
    }
  }

  // Windows specific -- when we are creating a .dll file, we also
  // need to create a .lib file. In MinGW mode, we only do that when the
  // -implib option is given explicitly, for compatibility with GNU ld.
  if (!config->exports.empty() || config->dll) {
    fixupExports();
    if (!config->mingw || !config->implib.empty())
      createImportLibrary(/*asLib=*/false);
    assignExportOrdinals();
  }

  // Handle /output-def (MinGW specific).
  if (auto *arg = args.getLastArg(OPT_output_def))
    writeDefFile(arg->getValue());

  // Set extra alignment for .comm symbols
  for (auto pair : config->alignComm) {
    StringRef name = pair.first;
    uint32_t alignment = pair.second;

    Symbol *sym = ctx.symtab.find(name);
    if (!sym) {
      warn("/aligncomm symbol " + name + " not found");
      continue;
    }

    // If the symbol isn't common, it must have been replaced with a regular
    // symbol, which will carry its own alignment.
    auto *dc = dyn_cast<DefinedCommon>(sym);
    if (!dc)
      continue;

    CommonChunk *c = dc->getChunk();
    c->setAlignment(std::max(c->getAlignment(), alignment));
  }

  // Windows specific -- Create an embedded or side-by-side manifest.
  // /manifestdependency: enables /manifest unless an explicit /manifest:no is
  // also passed.
  if (config->manifest == Configuration::Embed)
    addBuffer(createManifestRes(), false, false);
  else if (config->manifest == Configuration::SideBySide ||
           (config->manifest == Configuration::Default &&
            !config->manifestDependencies.empty()))
    createSideBySideManifest();

  // Handle /order. We want to do this at this moment because we
  // need a complete list of comdat sections to warn on nonexistent
  // functions.
  if (auto *arg = args.getLastArg(OPT_order)) {
    if (args.hasArg(OPT_call_graph_ordering_file))
      error("/order and /call-graph-order-file may not be used together");
    parseOrderFile(ctx, arg->getValue());
    config->callGraphProfileSort = false;
  }

  // Handle /call-graph-ordering-file and /call-graph-profile-sort (default on).
  if (config->callGraphProfileSort) {
    if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) {
      parseCallGraphFile(ctx, arg->getValue());
    }
    readCallGraphsFromObjectFiles(ctx);
  }

  // Handle /print-symbol-order.
  if (auto *arg = args.getLastArg(OPT_print_symbol_order))
    config->printSymbolOrder = arg->getValue();

  // Identify unreferenced COMDAT sections.
  if (config->doGC) {
    if (config->mingw) {
      // markLive doesn't traverse .eh_frame, but the personality function is
      // only reached that way. The proper solution would be to parse and
      // traverse the .eh_frame section, like the ELF linker does.
      // For now, just manually try to retain the known possible personality
      // functions. This doesn't bring in more object files, but only marks
      // functions that already have been included to be retained.
      for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0"}) {
        Defined *d = dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore(n));
        if (d && !d->isGCRoot) {
          d->isGCRoot = true;
          config->gcroot.push_back(d);
        }
      }
    }

    markLive(ctx);
  }

  // Needs to happen after the last call to addFile().
  convertResources();

  // Identify identical COMDAT sections to merge them.
  if (config->doICF != ICFLevel::None) {
    findKeepUniqueSections(ctx);
    doICF(ctx, config->doICF);
  }

  // Write the result.
  writeResult(ctx);

  // Stop early so we can print the results.
  rootTimer.stop();
  if (config->showTiming)
    ctx.rootTimer.print();
}