diff --git a/lld/MachO/Driver.h b/lld/MachO/Driver.h --- a/lld/MachO/Driver.h +++ b/lld/MachO/Driver.h @@ -36,6 +36,8 @@ #undef OPTION }; +void parseLCLinkerOption(InputFile*, unsigned argc, StringRef data); + std::string createResponseFile(const llvm::opt::InputArgList &args); // Check for both libfoo.dylib and libfoo.tbd (in that order). diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -332,6 +332,59 @@ return newFile; } +static void addLibrary(StringRef name, bool isWeak) { + if (Optional path = findLibrary(name)) { + auto *dylibFile = dyn_cast_or_null(addFile(*path, false)); + if (isWeak && dylibFile) + dylibFile->forceWeakImport = true; + return; + } + error("library not found for -l" + name); +} + +static void addFramework(StringRef name, bool isWeak) { + if (Optional path = findFramework(name)) { + auto *dylibFile = dyn_cast_or_null(addFile(*path, false)); + if (isWeak && dylibFile) + dylibFile->forceWeakImport = true; + return; + } + error("framework not found for -framework " + name); +} + +// Parses LC_LINKER_OPTION contents, which can add additional command line flags. +void macho::parseLCLinkerOption(InputFile* f, unsigned argc, StringRef data) { + SmallVector argv; + size_t offset = 0; + for (unsigned i = 0; i < argc && offset < data.size(); ++i) { + argv.push_back(data.data() + offset); + offset += strlen(data.data() + offset) + 1; + } + if (argv.size() != argc || offset > data.size()) + fatal(toString(f) + ": invalid LC_LINKER_OPTION"); + + MachOOptTable table; + unsigned missingIndex, missingCount; + opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount); + if (missingCount) + fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); + for (auto *arg : args.filtered(OPT_UNKNOWN)) + error("unknown argument: " + arg->getAsString(args)); + + for (auto *arg : args) { + switch (arg->getOption().getID()) { + case OPT_l: + addLibrary(arg->getValue(), false); + break; + case OPT_framework: + addFramework(arg->getValue(), false); + break; + default: + error(arg->getSpelling() + " is not allowed in LC_LINKER_OPTION"); + } + } +} + static void addFileList(StringRef path) { Optional buffer = readFile(path); if (!buffer) @@ -707,29 +760,13 @@ addFile(arg->getValue(), true); break; case OPT_l: - case OPT_weak_l: { - StringRef name = arg->getValue(); - if (Optional path = findLibrary(name)) { - auto *dylibFile = dyn_cast_or_null(addFile(*path, false)); - if (opt.getID() == OPT_weak_l && dylibFile) - dylibFile->forceWeakImport = true; - break; - } - error("library not found for -l" + name); + case OPT_weak_l: + addLibrary(arg->getValue(), opt.getID() == OPT_weak_l); break; - } case OPT_framework: - case OPT_weak_framework: { - StringRef name = arg->getValue(); - if (Optional path = findFramework(name)) { - auto *dylibFile = dyn_cast_or_null(addFile(*path, false)); - if (opt.getID() == OPT_weak_framework && dylibFile) - dylibFile->forceWeakImport = true; - break; - } - error("framework not found for -framework " + name); + case OPT_weak_framework: + addFramework(arg->getValue(), opt.getID() == OPT_weak_framework); break; - } case OPT_platform_version: handlePlatformVersion(arg); break; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -383,6 +383,13 @@ auto *buf = reinterpret_cast(mb.getBufferStart()); auto *hdr = reinterpret_cast(mb.getBufferStart()); + if (const load_command *cmd = findCommand(hdr, LC_LINKER_OPTION)) { + auto *c = reinterpret_cast(cmd); + StringRef data{reinterpret_cast(c + 1), + c->cmdsize - sizeof(linker_option_command)}; + parseLCLinkerOption(this, c->count, data); + } + if (const load_command *cmd = findCommand(hdr, LC_SEGMENT_64)) { auto *c = reinterpret_cast(cmd); sectionHeaders = ArrayRef{ diff --git a/lld/test/MachO/lc-linker-option.ll b/lld/test/MachO/lc-linker-option.ll new file mode 100644 --- /dev/null +++ b/lld/test/MachO/lc-linker-option.ll @@ -0,0 +1,55 @@ +# REQUIRES: x86 +# RUN: rm -rf %t +# RUN: split-file %s %t + +# RUN: llvm-as %t/framework.ll -o %t/framework.o +# RUN: %lld %t/framework.o -o %t/frame +# RUN: llvm-objdump --macho --all-headers %t/frame | FileCheck --check-prefix=FRAME %s +# FRAME: cmd LC_LOAD_DYLIB +# FRAME-NEXT: cmdsize +# FRAME-NEXT: name /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation + +# RUN: llvm-as %t/l.ll -o %t/l.o +# RUN: %lld %t/l.o -o %t/l +# RUN: llvm-objdump --macho --all-headers %t/l | FileCheck --check-prefix=LIB %s +# LIB: cmd LC_LOAD_DYLIB +# LIB-NEXT: cmdsize +# LIB-NEXT: name /usr/lib/libSystem.B.dylib + +# RUN: llvm-as %t/invalid.ll -o %t/invalid.o +# RUN: not %lld %t/invalid.o -o /dev/null 2>&1 | FileCheck --check-prefix=INVALID %s +# INVALID: error: -why_load is not allowed in LC_LINKER_OPTION + +#--- framework.ll +target triple = "x86_64-apple-macosx10.15.0" +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +!0 = !{!"-framework", !"CoreFoundation"} +!llvm.linker.options = !{!0} + +define void @main() { + ret void +} + +#--- l.ll +target triple = "x86_64-apple-macosx10.15.0" +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +!0 = !{!"-lSystem"} +!llvm.linker.options = !{!0} + +define void @main() { + ret void +} + +#--- invalid.ll + +target triple = "x86_64-apple-macosx10.15.0" +target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +!0 = !{!"-why_load"} +!llvm.linker.options = !{!0} + +define void @main() { + ret void +}