[lld-macho] Support -bundle

Not 100% sure but it appears that bundles are almost identical to
dylibs, aside from the fact that they do not contain `LC_ID_DYLIB`. ld64's code
seems to treat bundles and dylibs identically in most places.

Supporting bundles allows us to run e.g. XCTests, as all test suites are
compiled into bundles which get dynamically loaded by the `xctest` test runner.

Reviewed By: #lld-macho, smeenai

Differential Revision: https://reviews.llvm.org/D87856
This commit is contained in:
Jez Ng 2020-08-31 23:23:37 -07:00
parent e4e673e75a
commit f23f512691
3 changed files with 63 additions and 12 deletions

View File

@ -88,6 +88,24 @@ void MachOOptTable::printHelp(const char *argv0, bool showHidden) const {
lld::outs() << "\n";
}
HeaderFileType getOutputType(const opt::InputArgList &args) {
// TODO: -r, -dylinker, -preload...
opt::Arg *outputArg = args.getLastArg(OPT_bundle, OPT_dylib, OPT_execute);
if (outputArg == nullptr)
return MH_EXECUTE;
switch (outputArg->getOption().getID()) {
case OPT_bundle:
return MH_BUNDLE;
case OPT_dylib:
return MH_DYLIB;
case OPT_execute:
return MH_EXECUTE;
default:
llvm_unreachable("internal error");
}
}
static Optional<std::string>
findAlongPathsWithExtensions(StringRef name, ArrayRef<StringRef> extensions) {
llvm::SmallString<261> base;
@ -575,7 +593,7 @@ bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
config->headerPad = args::getHex(args, OPT_headerpad, /*Default=*/32);
config->headerPadMaxInstallNames =
args.hasArg(OPT_headerpad_max_install_names);
config->outputType = args.hasArg(OPT_dylib) ? MH_DYLIB : MH_EXECUTE;
config->outputType = getOutputType(args);
config->runtimePaths = args::getStrings(args, OPT_rpath);
config->allLoad = args.hasArg(OPT_all_load);
config->forceLoadObjC = args.hasArg(OPT_ObjC);
@ -661,6 +679,7 @@ bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
}
config->isPic = config->outputType == MH_DYLIB ||
config->outputType == MH_BUNDLE ||
(config->outputType == MH_EXECUTE && args.hasArg(OPT_pie));
// Now that all dylibs have been loaded, search for those that should be

View File

@ -377,6 +377,8 @@ void Writer::createLoadCommands() {
case MH_DYLIB:
in.header->addLoadCommand(make<LCDylib>(LC_ID_DYLIB, config->installName));
break;
case MH_BUNDLE:
break;
default:
llvm_unreachable("unhandled output file type");
}
@ -532,6 +534,7 @@ void Writer::createOutputSections() {
make<PageZeroSection>();
break;
case MH_DYLIB:
case MH_BUNDLE:
break;
default:
llvm_unreachable("unhandled output file type");

View File

@ -1,19 +1,48 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o
# RUN: lld -flavor darwinnew -o %t %t.o
# RUN: rm -rf %t && mkdir -p %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o
# RUN: lld -flavor darwinnew -o %t/executable %t/test.o
# RUN: lld -flavor darwinnew -bundle -o %t/bundle %t/test.o
# RUN: lld -flavor darwinnew -dylib -o %t/dylib %t/test.o
## These load commands should be in every final output binary.
# COMMON-DAG: cmd LC_DYLD_INFO_ONLY
# COMMON-DAG: cmd LC_SYMTAB
# COMMON-DAG: cmd LC_DYSYMTAB
## Check for the presence of load commands that are essential for a working
## executable.
# RUN: llvm-objdump --macho --all-headers %t | FileCheck %s
# CHECK-DAG: cmd LC_DYLD_INFO_ONLY
# CHECK-DAG: cmd LC_SYMTAB
# CHECK-DAG: cmd LC_DYSYMTAB
# CHECK-DAG: cmd LC_MAIN
# CHECK-DAG: cmd LC_LOAD_DYLINKER
## executable. Also check that it has the right filetype.
# RUN: llvm-objdump --macho --all-headers %t/executable | FileCheck %s --check-prefix=COMMON
# RUN: llvm-objdump --macho --all-headers %t/executable | FileCheck %s --check-prefix=EXEC
# EXEC: magic cputype cpusubtype caps filetype
# EXEC-NEXT: MH_MAGIC_64 X86_64 ALL {{.*}} EXECUTE
# EXEC-DAG: cmd LC_MAIN
# EXEC-DAG: cmd LC_LOAD_DYLINKER
## Check for the absence of load commands that should not be in an executable.
# RUN: llvm-objdump --macho --all-headers %t | FileCheck %s --check-prefix=NCHECK
# NCHECK-NOT: cmd: LC_ID_DYLIB
# RUN: llvm-objdump --macho --all-headers %t/executable | FileCheck %s --check-prefix=NEXEC
# NEXEC-NOT: cmd: LC_ID_DYLIB
## Check for the presence / absence of load commands for the dylib.
# RUN: llvm-objdump --macho --all-headers %t/dylib | FileCheck %s --check-prefix=COMMON
# RUN: llvm-objdump --macho --all-headers %t/dylib | FileCheck %s --check-prefix=DYLIB
# DYLIB: magic cputype cpusubtype caps filetype
# DYLIB-NEXT: MH_MAGIC_64 X86_64 ALL {{.*}} DYLIB
# DYLIB: cmd LC_ID_DYLIB
# RUN: llvm-objdump --macho --all-headers %t/bundle | FileCheck %s --check-prefix=NDYLIB
# NDYLIB-NOT: cmd: LC_MAIN
# NDYLIB-NOT: cmd: LC_LOAD_DYLINKER
## Check for the presence / absence of load commands for the bundle.
# RUN: llvm-objdump --macho --all-headers %t/bundle | FileCheck %s --check-prefix=COMMON
# RUN: llvm-objdump --macho --all-headers %t/bundle | FileCheck %s --check-prefix=BUNDLE
# BUNDLE: magic cputype cpusubtype caps filetype
# BUNDLE-NEXT: MH_MAGIC_64 X86_64 ALL {{.*}} BUNDLE
# RUN: llvm-objdump --macho --all-headers %t/bundle | FileCheck %s --check-prefix=NBUNDLE
# NBUNDLE-NOT: cmd: LC_MAIN
# NBUNDLE-NOT: cmd: LC_LOAD_DYLINKER
.text
.global _main