1240 lines
44 KiB
C++
1240 lines
44 KiB
C++
//===- 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/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 <algorithm>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <system_error>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
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<std::unique_ptr<File>>
|
|
makeErrorFile(StringRef path, std::error_code ec) {
|
|
std::vector<std::unique_ptr<File>> result;
|
|
result.push_back(llvm::make_unique<ErrorFile>(path, ec));
|
|
return result;
|
|
}
|
|
|
|
static std::vector<std::unique_ptr<File>>
|
|
parseMemberFiles(std::unique_ptr<File> file) {
|
|
std::vector<std::unique_ptr<File>> members;
|
|
if (auto *archive = dyn_cast<ArchiveLibraryFile>(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<std::unique_ptr<File>>
|
|
loadFile(MachOLinkingContext &ctx, StringRef path,
|
|
raw_ostream &diag, bool wholeArchive, bool upwardDylib) {
|
|
if (ctx.logInputFiles())
|
|
diag << path << "\n";
|
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = ctx.getMemoryBuffer(path);
|
|
if (std::error_code ec = mbOrErr.getError())
|
|
return makeErrorFile(path, ec);
|
|
ErrorOr<std::unique_ptr<File>> fileOrErr =
|
|
ctx.registry().loadFile(std::move(mbOrErr.get()));
|
|
if (std::error_code ec = fileOrErr.getError())
|
|
return makeErrorFile(path, ec);
|
|
std::unique_ptr<File> &file = fileOrErr.get();
|
|
|
|
// If file is a dylib, inform LinkingContext about it.
|
|
if (SharedLibraryFile *shl = dyn_cast<SharedLibraryFile>(file.get())) {
|
|
if (std::error_code ec = shl->parse())
|
|
return makeErrorFile(path, ec);
|
|
ctx.registerDylib(reinterpret_cast<mach_o::MachODylibFile *>(shl),
|
|
upwardDylib);
|
|
}
|
|
if (wholeArchive)
|
|
return parseMemberFiles(std::move(file));
|
|
std::vector<std::unique_ptr<File>> 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 = path;
|
|
std::replace(fixedPath.begin(), fixedPath.end(), sep, '/');
|
|
return fixedPath;
|
|
} else {
|
|
return path;
|
|
}
|
|
}
|
|
|
|
static void addFile(StringRef path, MachOLinkingContext &ctx,
|
|
bool loadWholeArchive,
|
|
bool upwardDylib, raw_ostream &diag) {
|
|
std::vector<std::unique_ptr<File>> files =
|
|
loadFile(ctx, path, diag, loadWholeArchive, upwardDylib);
|
|
for (std::unique_ptr<File> &file : files)
|
|
ctx.getNodes().push_back(llvm::make_unique<FileNode>(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,
|
|
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 llvm::Error loadFileList(StringRef fileListPath,
|
|
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 llvm::errorCodeToError(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 llvm::make_error<GenericError>(Twine("File not found '")
|
|
+ path
|
|
+ "'");
|
|
}
|
|
if (ctx.testingFileUsage()) {
|
|
diagnostics << "Found filelist entry " << canonicalizePath(path) << '\n';
|
|
}
|
|
addFile(path, ctx, forceLoad, false, diagnostics);
|
|
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::ParseCommandLineOptions(numArgs + 1, args);
|
|
}
|
|
}
|
|
|
|
namespace lld {
|
|
namespace mach_o {
|
|
|
|
bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx,
|
|
raw_ostream &diagnostics) {
|
|
// 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) {
|
|
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;
|
|
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) {
|
|
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) {
|
|
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(), args[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::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)) {
|
|
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
|
|
}
|
|
|
|
// 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)) {
|
|
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 ("
|
|
<< "0x" << llvm::utohexstr(ctx.pageSize()) << ")\n";
|
|
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) {
|
|
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;
|
|
}
|
|
uint16_t align = 1 << llvm::countTrailingZeros(alignValue);
|
|
if (!llvm::isPowerOf2_64(alignValue)) {
|
|
diagnostics << "warning: alignment for '-sectalign "
|
|
<< segName << " " << sectName
|
|
<< llvm::format(" 0x%llX", alignValue)
|
|
<< "' is not a power of two, using "
|
|
<< llvm::format("0x%08X", align) << "\n";
|
|
}
|
|
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)
|
|
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());
|
|
}
|
|
}
|
|
|
|
// 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<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 obsolete ObjC options: -objc_gc_compaction, -objc_gc, -objc_gc_only
|
|
if (parsedArgs.getLastArg(OPT_objc_gc_compaction)) {
|
|
diagnostics << "error: -objc_gc_compaction is not supported\n";
|
|
return false;
|
|
}
|
|
|
|
if (parsedArgs.getLastArg(OPT_objc_gc)) {
|
|
diagnostics << "error: -objc_gc is not supported\n";
|
|
return false;
|
|
}
|
|
|
|
if (parsedArgs.getLastArg(OPT_objc_gc_only)) {
|
|
diagnostics << "error: -objc_gc_only is not supported\n";
|
|
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)) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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 exectuables 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 exectuables 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 exectuables 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)) {
|
|
diagnostics << "error: malformed sdkVersion value\n";
|
|
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 an warning as we have no sdk
|
|
// version to put in that command.
|
|
// FIXME: We need to decide whether to make this an error.
|
|
diagnostics << "warning: -sdk_version is required when emitting "
|
|
"min version load command. "
|
|
"Setting sdk version to match provided min version\n";
|
|
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)) {
|
|
diagnostics << "error: malformed source_version value\n";
|
|
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)) {
|
|
diagnostics << "error: stack_size expects a hex number\n";
|
|
return false;
|
|
}
|
|
if ((stackSizeVal % ctx.pageSize()) != 0) {
|
|
diagnostics << "error: stack_size must be a multiple of page size ("
|
|
<< "0x" << llvm::utohexstr(ctx.pageSize()) << ")\n";
|
|
return false;
|
|
}
|
|
|
|
ctx.setStackSize(stackSizeVal);
|
|
}
|
|
|
|
// 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 -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 {
|
|
diagnostics << "error: invalid option to -undefined "
|
|
"[ warning | error | suppress | dynamic_lookup ]\n";
|
|
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) {
|
|
diagnostics << "error: can't use -undefined warning or suppress with "
|
|
"-twolevel_namespace\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ctx.setUndefinedMode(UndefMode);
|
|
}
|
|
|
|
// Handle -no_objc_category_merging.
|
|
if (parsedArgs.getLastArg(OPT_no_objc_category_merging))
|
|
ctx.setMergeObjCCategories(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());
|
|
}
|
|
}
|
|
|
|
// Parse the LLVM options before we process files in case the file handling
|
|
// makes use of things like DEBUG().
|
|
parseLLVMOptions(ctx);
|
|
|
|
// Handle input files and sectcreate.
|
|
for (auto &arg : parsedArgs) {
|
|
bool upward;
|
|
llvm::Optional<StringRef> resolvedPath;
|
|
switch (arg->getOption().getID()) {
|
|
default:
|
|
continue;
|
|
case OPT_INPUT:
|
|
addFile(arg->getValue(), ctx, globalWholeArchive, false, diagnostics);
|
|
break;
|
|
case OPT_upward_library:
|
|
addFile(arg->getValue(), ctx, false, true, diagnostics);
|
|
break;
|
|
case OPT_force_load:
|
|
addFile(arg->getValue(), ctx, true, false, diagnostics);
|
|
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.getValue()) << '\n';
|
|
}
|
|
addFile(resolvedPath.getValue(), ctx, globalWholeArchive,
|
|
upward, diagnostics);
|
|
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.getValue()) << '\n';
|
|
}
|
|
addFile(resolvedPath.getValue(), ctx, globalWholeArchive,
|
|
upward, diagnostics);
|
|
break;
|
|
case OPT_filelist:
|
|
if (auto ec = loadFileList(arg->getValue(),
|
|
ctx, globalWholeArchive,
|
|
diagnostics)) {
|
|
handleAllErrors(std::move(ec), [&](const llvm::ErrorInfoBase &EI) {
|
|
diagnostics << "error: ";
|
|
EI.log(diagnostics);
|
|
diagnostics << ", processing '-filelist " << arg->getValue() << "'\n";
|
|
});
|
|
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<std::unique_ptr<MemoryBuffer>> contentOrErr =
|
|
MemoryBuffer::getFile(fileName);
|
|
|
|
if (!contentOrErr) {
|
|
diagnostics << "error: can't open -sectcreate file " << fileName << "\n";
|
|
return false;
|
|
}
|
|
|
|
ctx.addSectCreateSection(seg, sect, std::move(*contentOrErr));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ctx.getNodes().empty()) {
|
|
diagnostics << "No input files\n";
|
|
return false;
|
|
}
|
|
|
|
// Validate the combination of options used.
|
|
return ctx.validate(diagnostics);
|
|
}
|
|
|
|
static void createFiles(MachOLinkingContext &ctx, bool Implicit) {
|
|
std::vector<std::unique_ptr<File>> 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(), llvm::make_unique<FileNode>(std::move(*i)));
|
|
}
|
|
}
|
|
|
|
/// This is where the link is actually performed.
|
|
bool link(llvm::ArrayRef<const char *> args, raw_ostream &diagnostics) {
|
|
MachOLinkingContext ctx;
|
|
if (!parse(args, ctx, diagnostics))
|
|
return false;
|
|
if (ctx.doNothing())
|
|
return true;
|
|
if (ctx.getNodes().empty())
|
|
return false;
|
|
|
|
for (std::unique_ptr<Node> &ie : ctx.getNodes())
|
|
if (FileNode *node = dyn_cast<FileNode>(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<SimpleFile> mergedFile = resolver.resultFile();
|
|
merged = mergedFile.get();
|
|
auto &members = ctx.getNodes();
|
|
members.insert(members.begin(),
|
|
llvm::make_unique<FileNode>(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.
|
|
diagnostics << "Failed to run passes on file '" << ctx.outputPath()
|
|
<< "': ";
|
|
logAllUnhandledErrors(std::move(ec), diagnostics, 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.
|
|
diagnostics << "Failed to write file '" << ctx.outputPath() << "': ";
|
|
logAllUnhandledErrors(std::move(ec), diagnostics, std::string());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // end namespace mach_o
|
|
} // end namespace lld
|