[lld-macho] Support -dyld_env
authorVy Nguyen <vyng@google.com>
Fri, 16 Sep 2022 17:38:20 +0000 (13:38 -0400)
committerVy Nguyen <vyng@google.com>
Tue, 20 Sep 2022 14:16:45 +0000 (10:16 -0400)
This arg is undocumented but from looking at the code + experiment, it's used to add additional DYLD_ENVIRONMENT load commands to the output.

Differential Revision: https://reviews.llvm.org/D134058

lld/MachO/Config.h
lld/MachO/Driver.cpp
lld/MachO/Options.td
lld/MachO/Writer.cpp
lld/test/MachO/dyld-env.s [new file with mode: 0644]
llvm/include/llvm/BinaryFormat/MachO.h

index c6e8b25..620206f 100644 (file)
@@ -206,6 +206,8 @@ struct Configuration {
 
   llvm::StringRef osoPrefix;
 
+  std::vector<llvm::StringRef> dyldEnvs;
+
   llvm::MachO::Architecture arch() const { return platformInfo.target.Arch; }
 
   llvm::MachO::PlatformType platform() const {
index 6bec108..f13649a 100644 (file)
@@ -1437,6 +1437,17 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
     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 <ENV_VAR>=<VALUE>, 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");
index 6af993a..e12626c 100644 (file)
@@ -984,6 +984,10 @@ def objc_stubs_fast : Flag<["-"], "objc_stubs_fast">,
 def objc_stubs_small : Flag<["-"], "objc_stubs_small">,
     HelpText<"Produce smaller stubs for Objective-C method calls with more jumps.">,
     Group<grp_rare>;
+def dyld_env : Separate<["-"], "dyld_env">,
+    MetaVarName<"<dyld_env_var>">,
+    HelpText<"Specifies a LC_DYLD_ENVIRONMENT variable value pair.">,
+    Group<grp_rare>;
 
 def grp_deprecated : OptionGroup<"deprecated">, HelpText<"DEPRECATED">;
 
@@ -1212,10 +1216,6 @@ def debug_snapshot : Flag<["-"], "debug_snapshot">,
     Group<grp_undocumented>;
 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<grp_undocumented>;
 def encryptable : Flag<["-"], "encryptable">,
     HelpText<"Generate the LC_ENCRYPTION_INFO load command">,
     Group<grp_undocumented>;
index a8ae554..d0207bb 100644 (file)
@@ -408,6 +408,31 @@ private:
   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<dyld_env_command *>(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 @@ template <class LP> void Writer::createLoadCommands() {
           make<LCDylib>(LC_REEXPORT_DYLIB, dylibFile->installName));
   }
 
+  for (const auto &dyldEnv : config->dyldEnvs)
+    in.header->addLoadCommand(make<LCDyldEnv>(dyldEnv));
+
   if (functionStartsSection)
     in.header->addLoadCommand(make<LCFunctionStarts>(functionStartsSection));
   if (dataInCodeSection)
diff --git a/lld/test/MachO/dyld-env.s b/lld/test/MachO/dyld-env.s
new file mode 100644 (file)
index 0000000..f86320c
--- /dev/null
@@ -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 <ENV_VAR>=<VALUE>, got `DYLD_FRAMEWORK_PATH,./Foo`
+
+# DYLIB: error: -dyld_env can only be used when creating executable output
+
+.section __TEXT,__text
+
+.global _main
+_main:
+  ret
index 4616daf..6205cd8 100644 (file)
@@ -869,6 +869,12 @@ struct build_version_command {
   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;