From f23f5126912b7da3f2a118a7cb1bcf6be3d8c1bc Mon Sep 17 00:00:00 2001 From: Jez Ng Date: Mon, 31 Aug 2020 23:23:37 -0700 Subject: [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 --- lld/MachO/Driver.cpp | 21 ++++++++++++++++- lld/MachO/Writer.cpp | 3 +++ lld/test/MachO/load-commands.s | 51 +++++++++++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 12 deletions(-) (limited to 'lld') diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index 38dc561..4991966 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -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 findAlongPathsWithExtensions(StringRef name, ArrayRef extensions) { llvm::SmallString<261> base; @@ -575,7 +593,7 @@ bool macho::link(llvm::ArrayRef 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 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 diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index 055e2f2..4572c52 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -377,6 +377,8 @@ void Writer::createLoadCommands() { case MH_DYLIB: in.header->addLoadCommand(make(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(); break; case MH_DYLIB: + case MH_BUNDLE: break; default: llvm_unreachable("unhandled output file type"); diff --git a/lld/test/MachO/load-commands.s b/lld/test/MachO/load-commands.s index c9f5d9b..3e9c07c 100644 --- a/lld/test/MachO/load-commands.s +++ b/lld/test/MachO/load-commands.s @@ -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 -- cgit v1.1