Skip to content

[lld][MachO] Respect dylibs linked with -allowable_client #114638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lld/MachO/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ struct Configuration {
llvm::StringRef finalOutput;

llvm::StringRef installName;
llvm::StringRef clientName;
llvm::StringRef mapFile;
llvm::StringRef ltoObjPath;
llvm::StringRef thinLTOJobs;
Expand Down
9 changes: 9 additions & 0 deletions lld/MachO/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1863,6 +1863,15 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
config->installName = config->finalOutput;
}

auto getClientName = [&]() {
StringRef cn = path::filename(config->finalOutput);
cn.consume_front("lib");
auto firstDotOrUnderscore = cn.find_first_of("._");
cn = cn.take_front(firstDotOrUnderscore);
return cn;
};
config->clientName = args.getLastArgValue(OPT_client_name, getClientName());

if (args.hasArg(OPT_mark_dead_strippable_dylib)) {
if (config->outputType != MH_DYLIB)
warn("-mark_dead_strippable_dylib: ignored, only has effect with -dylib");
Expand Down
20 changes: 20 additions & 0 deletions lld/MachO/DriverUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,26 @@ DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella,
if (newFile->exportingFile)
newFile->parseLoadCommands(mbref);
}

if (explicitlyLinked && !newFile->allowableClients.empty()) {
bool allowed = std::any_of(
newFile->allowableClients.begin(), newFile->allowableClients.end(),
[&](StringRef allowableClient) {
// We only do a prefix match to match LD64's behaviour.
return allowableClient.starts_with(config->clientName);
});

// TODO: This behaviour doesn't quite match the latest available source
// release of LD64 (ld64-951.9), which allows "parents" and "siblings"
// to link to libraries even when they're not explicitly named as
// allowable clients. However, behaviour around this seems to have
// changed in the latest release of Xcode (ld64-1115.7.3), so it's not
// clear what the correct thing to do is yet.
if (!allowed)
error("cannot link directly with '" +
sys::path::filename(newFile->installName) + "' because " +
config->clientName + " is not an allowed client");
}
return newFile;
}

Expand Down
14 changes: 14 additions & 0 deletions lld/MachO/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1730,6 +1730,14 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
? this
: this->umbrella;

if (!canBeImplicitlyLinked) {
for (auto *cmd : findCommands<sub_client_command>(hdr, LC_SUB_CLIENT)) {
StringRef allowableClient{reinterpret_cast<const char *>(cmd) +
cmd->client};
allowableClients.push_back(allowableClient);
}
}

const auto *dyldInfo = findCommand<dyld_info_command>(hdr, LC_DYLD_INFO_ONLY);
const auto *exportsTrie =
findCommand<linkedit_data_command>(hdr, LC_DYLD_EXPORTS_TRIE);
Expand Down Expand Up @@ -1891,6 +1899,12 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella,
exportingFile = (canBeImplicitlyLinked && isImplicitlyLinked(installName))
? this
: umbrella;

if (!canBeImplicitlyLinked)
for (const auto &allowableClient : interface.allowableClients())
allowableClients.push_back(
*make<std::string>(allowableClient.getInstallName().data()));

auto addSymbol = [&](const llvm::MachO::Symbol &symbol,
const Twine &name) -> void {
StringRef savedName = saver().save(name);
Expand Down
1 change: 1 addition & 0 deletions lld/MachO/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ class DylibFile final : public InputFile {
DylibFile *exportingFile = nullptr;
DylibFile *umbrella;
SmallVector<StringRef, 2> rpaths;
SmallVector<StringRef> allowableClients;
uint32_t compatibilityVersion = 0;
uint32_t currentVersion = 0;
int64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel
Expand Down
3 changes: 1 addition & 2 deletions lld/MachO/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -875,8 +875,7 @@ def allowable_client : Separate<["-"], "allowable_client">,
Group<grp_rare>;
def client_name : Separate<["-"], "client_name">,
MetaVarName<"<name>">,
HelpText<"Specifies a <name> this client should match with the -allowable_client <name> in a dependent dylib">,
Flags<[HelpHidden]>,
HelpText<"Specifies a <name> this client should match with the -allowable_client <name> in an explicitly linked dylib">,
Group<grp_rare>;
def umbrella : Separate<["-"], "umbrella">,
MetaVarName<"<name>">,
Expand Down
Binary file not shown.
74 changes: 74 additions & 0 deletions lld/test/MachO/allowable-client.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# REQUIRES: x86
# RUN: rm -rf %t; split-file %s %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o

# Check linking against a .dylib
# RUN: not %lld -o %t/test %t/test.o -L%S/Inputs -lallowable_client 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-IMPLICIT
# RUN: not %lld -o %t/libtest_debug.exe %t/test.o -L%S/Inputs -lallowable_client 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-IMPLICIT
# RUN: not %lld -o %t/test %t/test.o -L%S/Inputs -lallowable_client -client_name notallowed 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-EXPLICIT
# RUN: %lld -o %t/test %t/test.o -L%S/Inputs -lallowable_client -client_name allowed
# RUN: %lld -o %t/test %t/test.o -L%S/Inputs -lallowable_client -client_name all
# RUN: %lld -o %t/all %t/test.o -L%S/Inputs -lallowable_client
# RUN: %lld -o %t/allowed %t/test.o -L%S/Inputs -lallowable_client
# RUN: %lld -o %t/liballowed_debug.exe %t/test.o -L%S/Inputs -lallowable_client

# Check linking against a .tbd
# RUN: not %lld -o %t/test %t/test.o -L%t -lallowable_client 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-IMPLICIT
# RUN: not %lld -o %t/libtest_debug.exe %t/test.o -L%t -lallowable_client 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-IMPLICIT
# RUN: not %lld -o %t/test %t/test.o -L%t -lallowable_client -client_name notallowed 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-EXPLICIT
# RUN: %lld -o %t/test %t/test.o -L%t -lallowable_client -client_name allowed
# RUN: %lld -o %t/test %t/test.o -L%t -lallowable_client -client_name all
# RUN: %lld -o %t/all %t/test.o -L%t -lallowable_client
# RUN: %lld -o %t/allowed %t/test.o -L%t -lallowable_client
# RUN: %lld -o %t/liballowed_debug.exe %t/test.o -L%t -lallowable_client

# NOTALLOWED-IMPLICIT: error: cannot link directly with 'liballowable_client.dylib' because test is not an allowed client
# NOTALLOWED-EXPLICIT: error: cannot link directly with 'liballowable_client.dylib' because notallowed is not an allowed client

#--- test.s
.text
.globl _main
_main:
ret

#--- liballowable_client.tbd
{
"main_library": {
"allowable_clients": [
{
"clients": [
"allowed"
]
}
],
"compatibility_versions": [
{
"version": "0"
}
],
"current_versions": [
{
"version": "0"
}
],
"flags": [
{
"attributes": [
"not_app_extension_safe"
]
}
],
"install_names": [
{
"name": "lib/liballowable_client.dylib"
}
],
"target_info": [
{
"min_deployment": "10.11",
"target": "x86_64-macos"
}
]
},
"tapi_tbd_version": 5
}
Loading