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 @@ -1410,6 +1410,20 @@ addFile(arg->getValue(), LoadType::CommandLine, /*isLazy=*/false, /*isExplicit=*/false, /*isBundleLoader=*/true); } + if (const Arg *arg = args.getLastArg(OPT_dyld_env)) { + if (config->outputType != MH_EXECUTE) + error("-dyld_env can only be used when creating executable output"); + // Validate that the argument is in the form + // DYLD__PATH=. + StringRef dyldEnv = saver().save(arg->getValue()); + if (!dyldEnv.startswith("DYLD_") || !dyldEnv.contains("_PATH=")) + error("-dyld_env malformed value. Expected " + "-dyld_env=DYLD__PATH=, got " + + Twine(dyldEnv)); + else + config->dyldEnvs.push_back(dyldEnv); + } + 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 @@ -1212,10 +1212,12 @@ 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 dyld_env : Separate<["-"], "dyld_env">, + MetaVarName<"">, + HelpText<"Specifies a LC_DYLD_ENVIRONMENT variable value pair.">, + 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 @@ -407,6 +407,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(rpath_command); + + c->cmd = LC_DYLD_ENVIRONMENT; + c->cmdsize = getSize(); + c->path = 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) @@ -821,6 +846,11 @@ make(LC_REEXPORT_DYLIB, dylibFile->installName)); } + if (!config->dyldEnvs.empty()) { + 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,39 @@ +# REQUIRES: x86_64, shell +## Requires 64 bit because the cmdsize is platform dependent. + +# RUN: rm -rf %t; split-file %s %t +# RUN: mkdir %t/subdir + +# RUN: llvm-mc -filetype obj -triple x86_64-apple-darwin %t/main.s -o %t/main.o +# RUN: llvm-mc -filetype obj -triple x86_64-apple-darwin %t/foo.s -o %t/foo.o + +# RUN: mkdir -p %t/Foo.framework/Versions/A +# RUN: %lld -dylib -install_name %t/Foo.framework/Versions/A/Foo %t/foo.o -o %t/Foo.framework/Versions/A/Foo +# RUN: ln -sf A %t/Foo.framework/Versions/Current +# RUN: ln -sf Versions/Current/Foo %t/Foo.framework/Foo + + +# RUN: %lld -lSystem -dyld_env DYLD_FRAMEWORK_PATH=%t/Foo.framework %t/main.o -o %t/main.out +# RUN: llvm-otool -l %t/main.out | FileCheck %s -DVALUE=%t/Foo.framework + +# RUN: not %lld -lSystem -dyld_env DYLD_FRAMEWORK_PATH,%t/Foo %t/main.o -o /dev/null 2>&1 | FileCheck %s -DVALUE=%t/Foo --check-prefix=ERR + +CHECK: Load command 11 +CHECK-NEXT: cmd LC_DYLD_ENVIRONMENT +CHECK-NEXT: cmdsize 128 +CHECK-NEXT: name DYLD_FRAMEWORK_PATH=[[VALUE]] (offset 12) + +ERR: error: -dyld_env malformed value. Expected -dyld_env=DYLD__PATH=, got DYLD_FRAMEWORK_PATH,[[VALUE]] + + +#--- foo.s +.globl _foo +_foo: + retq + +#--- main.s +.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;