diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -206,6 +206,8 @@ llvm::StringRef osoPrefix; + std::vector dyldEnvs; + llvm::MachO::Architecture arch() const { return platformInfo.target.Arch; } llvm::MachO::PlatformType platform() const { diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -1437,6 +1437,17 @@ addFile(arg->getValue(), LoadType::CommandLine, /*isLazy=*/false, /*isExplicit=*/false, /*isBundleLoader=*/true); } + for (auto *arg : args.filtered(OPT_dyld_env)) { + StringRef envPair(arg->getValue()); + if (!envPair.contains('=')) + error("-dyld_env's argument is malformed. Expected " + "-dyld_env =, got `" + + envPair + "`"); + config->dyldEnvs.push_back(envPair); + } + if (!config->dyldEnvs.empty() && config->outputType != MH_EXECUTE) + error("-dyld_env can only be used when creating executable output"); + if (const Arg *arg = args.getLastArg(OPT_umbrella)) { if (config->outputType != MH_DYLIB) warn("-umbrella used, but not creating dylib"); diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -984,6 +984,10 @@ def objc_stubs_small : Flag<["-"], "objc_stubs_small">, HelpText<"Produce smaller stubs for Objective-C method calls with more jumps.">, Group; +def dyld_env : Separate<["-"], "dyld_env">, + MetaVarName<"">, + HelpText<"Specifies a LC_DYLD_ENVIRONMENT variable value pair.">, + Group; def grp_deprecated : OptionGroup<"deprecated">, HelpText<"DEPRECATED">; @@ -1212,10 +1216,6 @@ Group; def demangle : Flag<["-"], "demangle">, HelpText<"Demangle symbol names in diagnostics">; -def dyld_env : Flag<["-"], "dyld_env">, - HelpText<"This option is undocumented in ld64">, - Flags<[HelpHidden]>, - Group; def encryptable : Flag<["-"], "encryptable">, HelpText<"Generate the LC_ENCRYPTION_INFO load command">, Group; diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -408,6 +408,31 @@ StringRef path; }; +class LCDyldEnv final : public LoadCommand { +public: + explicit LCDyldEnv(StringRef name) : name(name) {} + + uint32_t getSize() const override { + return alignTo(sizeof(dyld_env_command) + name.size() + 1, + target->wordSize); + } + + void writeTo(uint8_t *buf) const override { + auto *c = reinterpret_cast(buf); + buf += sizeof(dyld_env_command); + + c->cmd = LC_DYLD_ENVIRONMENT; + c->cmdsize = getSize(); + c->name = sizeof(dyld_env_command); + + memcpy(buf, name.data(), name.size()); + buf[name.size()] = '\0'; + } + +private: + StringRef name; +}; + class LCMinVersion final : public LoadCommand { public: explicit LCMinVersion(const PlatformInfo &platformInfo) @@ -822,6 +847,9 @@ make(LC_REEXPORT_DYLIB, dylibFile->installName)); } + for (const auto &dyldEnv : config->dyldEnvs) + in.header->addLoadCommand(make(dyldEnv)); + if (functionStartsSection) in.header->addLoadCommand(make(functionStartsSection)); if (dataInCodeSection) diff --git a/lld/test/MachO/dyld-env.s b/lld/test/MachO/dyld-env.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/dyld-env.s @@ -0,0 +1,41 @@ +# REQUIRES: x86 + +# RUN: rm -rf %t && mkdir %t + +# RUN: llvm-mc -filetype obj -triple x86_64-apple-darwin %s -o %t/main.o + +# RUN: %lld -lSystem -dyld_env DYLD_FRAMEWORK_PATH=./Foo.framework %t/main.o -o %t/one_dyld_env.out +# RUN: llvm-otool -l %t/one_dyld_env.out | FileCheck %s --check-prefix=ONE-ENV + +# RUN: %lld -lSystem -dyld_env DYLD_FRAMEWORK_PATH=./Foo.framework \ +# RUN: -dyld_env DYLD_FRAMEWORK_PATH=./Bar.framework \ +# RUN: %t/main.o -o %t/two_dyld_envs.out +# RUN: llvm-otool -l %t/two_dyld_envs.out | FileCheck %s --check-prefix=TWO-ENV + +# RUN: not %lld -lSystem -dyld_env DYLD_FRAMEWORK_PATH,./Foo %t/main.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=MALFORMED + +# RUN: not %lld -dylib -lSystem -dyld_env DYLD_FRAMEWORK_PATH=./Foo %t/main.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=DYLIB + +# ONE-ENV: Load command 11 +# ONE-ENV-NEXT: cmd LC_DYLD_ENVIRONMENT +# ONE-ENV-NEXT: cmdsize 48 +# ONE-ENV-NEXT: name DYLD_FRAMEWORK_PATH=./Foo.framework (offset 12) + +# TWO-ENV: Load command 11 +# TWO-ENV-NEXT: cmd LC_DYLD_ENVIRONMENT +# TWO-ENV-NEXT: cmdsize 48 +# TWO-ENV-NEXT: name DYLD_FRAMEWORK_PATH=./Foo.framework (offset 12) +# TWO-ENV-NEXT: Load command 12 +# TWO-ENV-NEXT: cmd LC_DYLD_ENVIRONMENT +# TWO-ENV-NEXT: cmdsize 48 +# TWO-ENV-NEXT: name DYLD_FRAMEWORK_PATH=./Bar.framework (offset 12) + +# MALFORMED: error: -dyld_env's argument is malformed. Expected -dyld_env =, got `DYLD_FRAMEWORK_PATH,./Foo` + +# DYLIB: error: -dyld_env can only be used when creating executable output + +.section __TEXT,__text + +.global _main +_main: + ret diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h --- a/llvm/include/llvm/BinaryFormat/MachO.h +++ b/llvm/include/llvm/BinaryFormat/MachO.h @@ -869,6 +869,12 @@ uint32_t ntools; // number of tool entries following this }; +struct dyld_env_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t name; +}; + struct dyld_info_command { uint32_t cmd; uint32_t cmdsize;