Index: lld/MachO/Arch/ARM.cpp =================================================================== --- lld/MachO/Arch/ARM.cpp +++ lld/MachO/Arch/ARM.cpp @@ -160,6 +160,7 @@ ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) { cpuType = CPU_TYPE_ARM; this->cpuSubtype = cpuSubtype; + threadCommandFlavor = ARM_THREAD_STATE; stubSize = 0 /* FIXME */; stubHelperHeaderSize = 0 /* FIXME */; Index: lld/MachO/Arch/ARM64.cpp =================================================================== --- lld/MachO/Arch/ARM64.cpp +++ lld/MachO/Arch/ARM64.cpp @@ -132,6 +132,7 @@ ARM64::ARM64() : ARM64Common(LP64()) { cpuType = CPU_TYPE_ARM64; cpuSubtype = CPU_SUBTYPE_ARM64_ALL; + threadCommandFlavor = ARM_THREAD_STATE64; stubSize = sizeof(stubCode); thunkSize = sizeof(thunkCode); Index: lld/MachO/Arch/ARM64_32.cpp =================================================================== --- lld/MachO/Arch/ARM64_32.cpp +++ lld/MachO/Arch/ARM64_32.cpp @@ -104,6 +104,7 @@ ARM64_32::ARM64_32() : ARM64Common(ILP32()) { cpuType = CPU_TYPE_ARM64_32; cpuSubtype = CPU_SUBTYPE_ARM64_V8; + threadCommandFlavor = ARM_THREAD_STATE64; modeDwarfEncoding = 0x04000000; // UNWIND_ARM_MODE_DWARF subtractorRelocType = GENERIC_RELOC_INVALID; // FIXME Index: lld/MachO/Arch/X86_64.cpp =================================================================== --- lld/MachO/Arch/X86_64.cpp +++ lld/MachO/Arch/X86_64.cpp @@ -185,6 +185,7 @@ X86_64::X86_64() : TargetInfo(LP64()) { cpuType = CPU_TYPE_X86_64; cpuSubtype = CPU_SUBTYPE_X86_64_ALL; + threadCommandFlavor = x86_THREAD_STATE64; modeDwarfEncoding = UNWIND_X86_MODE_DWARF; subtractorRelocType = X86_64_RELOC_SUBTRACTOR; Index: lld/MachO/Config.h =================================================================== --- lld/MachO/Config.h +++ lld/MachO/Config.h @@ -104,6 +104,7 @@ struct Configuration { Symbol *entry = nullptr; bool hasReexports = false; + bool addLoadVersion = true; bool allLoad = false; bool applicationExtension = false; bool archMultiple = false; Index: lld/MachO/Driver.cpp =================================================================== --- lld/MachO/Driver.cpp +++ lld/MachO/Driver.cpp @@ -1245,6 +1245,9 @@ for (const Arg *arg : args.filtered(OPT_U)) config->explicitDynamicLookups.insert(arg->getValue()); + if (const Arg *arg = args.getLastArg(OPT_static, OPT_dynamic)) + config->staticLink = arg->getOption().getID() == OPT_static; + config->mapFile = args.getLastArgValue(OPT_map); config->optimize = args::getInteger(args, OPT_O, 1); config->outputFile = args.getLastArgValue(OPT_o, "a.out"); @@ -1289,11 +1292,7 @@ config->deadStripDylibs = args.hasArg(OPT_dead_strip_dylibs); config->demangle = args.hasArg(OPT_demangle); config->implicitDylibs = !args.hasArg(OPT_no_implicit_dylibs); - config->emitFunctionStarts = - args.hasFlag(OPT_function_starts, OPT_no_function_starts, true); config->emitBitcodeBundle = args.hasArg(OPT_bitcode_bundle); - config->emitDataInCodeInfo = - args.hasFlag(OPT_data_in_code_info, OPT_no_data_in_code_info, true); config->icfLevel = getICFLevel(args); config->dedupLiterals = args.hasFlag(OPT_deduplicate_literals, OPT_icf_eq, false) || @@ -1305,6 +1304,13 @@ config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); config->parseEhFrames = static_cast(getenv("LLD_IN_TEST")); + config->addLoadVersion = + args.hasFlag(OPT_version_load_command, OPT_INVALID, !config->staticLink); + config->emitFunctionStarts = args.hasFlag( + OPT_function_starts, OPT_no_function_starts, !config->staticLink); + config->emitDataInCodeInfo = args.hasFlag( + OPT_data_in_code_info, OPT_no_data_in_code_info, !config->staticLink); + // FIXME: Add a commandline flag for this too. config->zeroModTime = getenv("ZERO_AR_DATE"); @@ -1312,7 +1318,8 @@ PLATFORM_IOS, PLATFORM_WATCHOS, PLATFORM_TVOS}; config->emitEncryptionInfo = args.hasFlag(OPT_encryptable, OPT_no_encryption, - is_contained(encryptablePlatforms, config->platform())); + is_contained(encryptablePlatforms, config->platform()) && + !config->staticLink); #ifndef LLVM_HAVE_LIBXAR if (config->emitBitcodeBundle) @@ -1337,9 +1344,6 @@ config->markDeadStrippableDylib = true; } - if (const Arg *arg = args.getLastArg(OPT_static, OPT_dynamic)) - config->staticLink = (arg->getOption().getID() == OPT_static); - if (const Arg *arg = args.getLastArg(OPT_flat_namespace, OPT_twolevel_namespace)) config->namespaceKind = arg->getOption().getID() == OPT_twolevel_namespace @@ -1348,10 +1352,13 @@ config->undefinedSymbolTreatment = getUndefinedSymbolTreatment(args); - if (config->outputType == MH_EXECUTE) - config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"), - /*file=*/nullptr, - /*isWeakRef=*/false); + if (config->outputType == MH_EXECUTE) { + const char *entry_point = config->staticLink ? "start" : "_main"; + config->entry = + symtab->addUndefined(args.getLastArgValue(OPT_e, entry_point), + /*file=*/nullptr, + /*isWeakRef=*/false); + } config->librarySearchPaths = getLibrarySearchPaths(args, config->systemLibraryRoots); @@ -1508,7 +1515,7 @@ config->isPic = config->outputType == MH_DYLIB || config->outputType == MH_BUNDLE || (config->outputType == MH_EXECUTE && - args.hasFlag(OPT_pie, OPT_no_pie, true)); + args.hasFlag(OPT_pie, OPT_no_pie, !config->staticLink)); // Now that all dylibs have been loaded, search for those that should be // re-exported. Index: lld/MachO/Options.td =================================================================== --- lld/MachO/Options.td +++ lld/MachO/Options.td @@ -124,7 +124,6 @@ Group; def static : Flag<["-"], "static">, HelpText<"Link statically">, - Flags<[HelpHidden]>, Group; def preload : Flag<["-"], "preload">, HelpText<"Produce an unsegmented binary for embedded systems">, @@ -1320,7 +1319,6 @@ Group; def version_load_command : Flag<["-"], "version_load_command">, HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, Group; def grp_ignored : OptionGroup<"ignored">, HelpText<"IGNORED">; Index: lld/MachO/Target.h =================================================================== --- lld/MachO/Target.h +++ lld/MachO/Target.h @@ -81,6 +81,7 @@ uint32_t magic; llvm::MachO::CPUType cpuType; uint32_t cpuSubtype; + uint32_t threadCommandFlavor = 0; uint64_t pageZeroSize; size_t headerSize; Index: lld/MachO/Writer.cpp =================================================================== --- lld/MachO/Writer.cpp +++ lld/MachO/Writer.cpp @@ -313,6 +313,75 @@ StringTableSection *stringTableSection = nullptr; }; +class LCUnixThread final : public LoadCommand { + uint32_t getFlavorSize() const { + switch (target->cpuType & ~CPU_ARCH_MASK) { + case CPU_TYPE_I386: + switch (target->threadCommandFlavor) { + case x86_THREAD_STATE64: + return sizeof(x86_thread_state64_t); + } + break; + case CPU_TYPE_ARM: + switch (target->threadCommandFlavor) { + case ARM_THREAD_STATE: + return sizeof(arm_thread_state32_t); + case ARM_THREAD_STATE64: + return sizeof(arm_thread_state64_t); + } + break; + } + + fatal("Unknown flavor " + Twine::utohexstr(target->threadCommandFlavor)); + return 0; + } + + uint32_t getSize() const override { + return sizeof(thread_command) + sizeof(x86_state_hdr_t) + getFlavorSize(); + } + + void writeTo(uint8_t *buf) const override { + auto *c = reinterpret_cast(buf); + c->cmd = LC_UNIXTHREAD; + c->cmdsize = getSize(); + + auto *hdr = + reinterpret_cast(buf + sizeof(thread_command)); + hdr->flavor = target->threadCommandFlavor; + + // Yes, this is what the macOS SDK does. + hdr->count = getFlavorSize() / sizeof(uint32_t); + buf = reinterpret_cast(hdr + 1); + memset(buf, 0, getFlavorSize()); + + switch (target->cpuType & ~CPU_ARCH_MASK) { + case CPU_TYPE_I386: + switch (target->threadCommandFlavor) { + case x86_THREAD_STATE64: { + auto *state = reinterpret_cast(buf); + state->rip = config->entry->getVA(); + break; + } + } + break; + case CPU_TYPE_ARM: + switch (target->threadCommandFlavor) { + case ARM_THREAD_STATE: { + auto *state = reinterpret_cast(buf); + state->pc = config->entry->getVA(); + break; + } + case ARM_THREAD_STATE64: { + auto *state = reinterpret_cast(buf); + state->pc = config->entry->getVA(); + break; + } + } + break; + } + } +}; + // There are several dylib load commands that share the same structure: // * LC_LOAD_DYLIB // * LC_ID_DYLIB @@ -736,11 +805,13 @@ seg->index = segIndex++; } - in.header->addLoadCommand(make( - in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports)); + if (!config->staticLink) + in.header->addLoadCommand(make( + in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports)); in.header->addLoadCommand(make(symtabSection, stringTableSection)); - in.header->addLoadCommand( - make(symtabSection, indirectSymtabSection)); + if (!config->staticLink || (config->staticLink && config->isPic)) + in.header->addLoadCommand( + make(symtabSection, indirectSymtabSection)); if (!config->umbrella.empty()) in.header->addLoadCommand(make(config->umbrella)); if (config->emitEncryptionInfo) @@ -750,7 +821,8 @@ switch (config->outputType) { case MH_EXECUTE: - in.header->addLoadCommand(make()); + if (!config->staticLink) + in.header->addLoadCommand(make()); break; case MH_DYLIB: in.header->addLoadCommand(make(LC_ID_DYLIB, config->installName, @@ -766,10 +838,12 @@ uuidCommand = make(); in.header->addLoadCommand(uuidCommand); - if (useLCBuildVersion(config->platformInfo)) - in.header->addLoadCommand(make(config->platformInfo)); - else - in.header->addLoadCommand(make(config->platformInfo)); + if (config->addLoadVersion) { + if (useLCBuildVersion(config->platformInfo)) + in.header->addLoadCommand(make(config->platformInfo)); + else + in.header->addLoadCommand(make(config->platformInfo)); + } if (config->secondaryPlatformInfo) { in.header->addLoadCommand( @@ -777,8 +851,12 @@ } // This is down here to match ld64's load command order. - if (config->outputType == MH_EXECUTE) - in.header->addLoadCommand(make()); + if (config->outputType == MH_EXECUTE) { + if (config->staticLink) + in.header->addLoadCommand(make()); + else + in.header->addLoadCommand(make()); + } int64_t dylibOrdinal = 1; DenseMap ordinalForInstallName; Index: lld/test/MachO/static.s =================================================================== --- /dev/null +++ lld/test/MachO/static.s @@ -0,0 +1,63 @@ +# REQUIRES: x86, aarch64 +## If these tests start failing because `LC_SOURCE_VERSION` is added, please +## update the `Load command` lines. Not emitting `LC_SOURCE_VERSION` to fix +## this tests is the wrong behavior. + +# RUN: rm -rf %t; mkdir %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/x86_64.o +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t/arm64.o +# RUN: llvm-mc -filetype=obj -triple=arm64_32-apple-darwin %s -o %t/arm64_32.o + +# RUN: %lld -static -arch x86_64 -o %t/x86_64 %t/x86_64.o +# RUN: llvm-objdump --macho --private-headers %t/x86_64 | FileCheck %s -DARCH=x86 -D#SIZE=184 +# RUN: %lld -static -arch arm64 -o %t/arm64 %t/arm64.o +# RUN: llvm-objdump --macho --private-headers %t/arm64 | FileCheck %s -DARCH=ARM -D#SIZE=288 +# RUN: %lld-watchos -static -arch arm64_32 -o %t/arm64_32 %t/arm64_32.o +# RUN: llvm-objdump --macho --private-headers %t/arm64_32 | FileCheck %s -DARCH=ARM -D#SIZE=288 + +# CHECK-NOT: cmd LC_DYSYMTAB +# CHECK-NOT: cmd LC_ENCRYPTION_INFO +# CHECK-NOT: cmd LC_ENCRYPTION_INFO_64 +# CHECK-NOT: cmd LC_LOAD_DYLINKER +# CHECK-NOT: cmd LC_BUILD_VERSION +# CHECK-NOT: cmd LC_VERSION_MIN_MACOSX +# CHECK-NOT: cmd LC_VERSION_MIN_IPHONEOS +# CHECK-NOT: cmd LC_VERSION_MIN_TVOS +# CHECK-NOT: cmd LC_VERSION_MIN_WATCHOS +# CHECK: Load command 5 +# CHECK-NEXT: cmd LC_UNIXTHREAD +# CHECK-NEXT: cmdsize [[#%d,SIZE]] +# CHECK-NEXT: flavor [[ARCH]]_THREAD_STATE64 +# CHECK-NEXT: count [[ARCH]]_THREAD_STATE64_COUNT +# CHECK-NOT: cmd LC_FUNCTION_STARTS +# CHECK-NOT: cmd LC_DATA_IN_CODE + +# RUN: %lld -static -arch x86_64 -o %t/x86_64-pie %t/x86_64.o -pie +# RUN: llvm-objdump --macho --private-headers %t/x86_64-pie | FileCheck %s --check-prefix=CHECK-PIE -DARCH=x86 -D#SIZE=184 + +# CHECK-PIE: Load command 4 +# CHECK-PIE-NEXT: cmd LC_DYSYMTAB +# CHECK-PIE-NEXT: cmdsize 80 +# CHECK-PIE: Load command 6 +# CHECK-PIE-NEXT: cmd LC_UNIXTHREAD +# CHECK-PIE-NEXT: cmdsize [[#%d,SIZE]] +# CHECK-PIE-NEXT: flavor [[ARCH]]_THREAD_STATE64 +# CHECK-PIE-NEXT: count [[ARCH]]_THREAD_STATE64_COUNT + +# RUN: %lld -static -arch x86_64 -o %t/x86_64-vlc %t/x86_64.o -version_load_command +# RUN: llvm-objdump --macho --private-headers %t/x86_64-vlc | FileCheck %s --check-prefix=CHECK-VLC -DARCH=x86 -D#SIZE=184 -DOS=macos +# RUN: %lld-watchos -static -arch arm64_32 -o %t/arm64_32-vlc %t/arm64_32.o -version_load_command +# RUN: llvm-objdump --macho --private-headers %t/arm64_32-vlc | FileCheck %s --check-prefix=CHECK-VLC -DARCH=ARM -D#SIZE=288 -DOS=watchos + +# CHECK-VLC: Load command 5 +# CHECK-VLC-NEXT: cmd LC_BUILD_VERSION +# CHECK-VLC-NEXT: cmdsize 32 +# CHECK-VLC-NEXT: platform [[OS]] +# CHECK-VLC: Load command 6 +# CHECK-VLC-NEXT: cmd LC_UNIXTHREAD +# CHECK-VLC-NEXT: cmdsize [[#%d,SIZE]] +# CHECK-VLC-NEXT: flavor [[ARCH]]_THREAD_STATE64 +# CHECK-VLC-NEXT: count [[ARCH]]_THREAD_STATE64_COUNT + +.globl start +start: