Skip to content

Commit

Permalink
[mac/lld] Add support for the LC_LINKER_OPTION load command in o files
Browse files Browse the repository at this point in the history
clang puts `-framework CoreFoundation` in this load command for files
that use @available / __builtin_available. Without support for this,
binaries that don't explicitly link to CoreFoundation fail to link.

Differential Revision: https://reviews.llvm.org/D92624
  • Loading branch information
nico committed Dec 4, 2020
1 parent 0519e1d commit 16b1f6e
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 20 deletions.
77 changes: 57 additions & 20 deletions lld/MachO/Driver.cpp
Expand Up @@ -332,6 +332,59 @@ static InputFile *addFile(StringRef path, bool forceLoadArchive) {
return newFile;
}

static void addLibrary(StringRef name, bool isWeak) {
if (Optional<std::string> path = findLibrary(name)) {
auto *dylibFile = dyn_cast_or_null<DylibFile>(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<std::string> path = findFramework(name)) {
auto *dylibFile = dyn_cast_or_null<DylibFile>(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<const char *, 4> 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<MemoryBufferRef> buffer = readFile(path);
if (!buffer)
Expand Down Expand Up @@ -707,29 +760,13 @@ bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
addFile(arg->getValue(), true);
break;
case OPT_l:
case OPT_weak_l: {
StringRef name = arg->getValue();
if (Optional<std::string> path = findLibrary(name)) {
auto *dylibFile = dyn_cast_or_null<DylibFile>(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<std::string> path = findFramework(name)) {
auto *dylibFile = dyn_cast_or_null<DylibFile>(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;
Expand Down
2 changes: 2 additions & 0 deletions lld/MachO/Driver.h
Expand Up @@ -36,6 +36,8 @@ enum {
#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).
Expand Down
7 changes: 7 additions & 0 deletions lld/MachO/InputFiles.cpp
Expand Up @@ -383,6 +383,13 @@ ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName)
auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
auto *hdr = reinterpret_cast<const mach_header_64 *>(mb.getBufferStart());

if (const load_command *cmd = findCommand(hdr, LC_LINKER_OPTION)) {
auto *c = reinterpret_cast<const linker_option_command *>(cmd);
StringRef data{reinterpret_cast<const char *>(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<const segment_command_64 *>(cmd);
sectionHeaders = ArrayRef<section_64>{
Expand Down
55 changes: 55 additions & 0 deletions 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
}

0 comments on commit 16b1f6e

Please sign in to comment.