[lld-macho] Support -dyld_env

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
This commit is contained in:
Vy Nguyen 2022-09-16 13:38:20 -04:00
parent 1a18fe65d3
commit 016c2f5e32
6 changed files with 92 additions and 4 deletions

View 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 {

View 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");

View 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>;

View 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)

41
lld/test/MachO/dyld-env.s Normal file
View File

@ -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

View 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;