Skip to content

Commit 1de9bc1

Browse files
authored
[lld][MachO] Respect dylibs linked with -allowable_client (#114638)
ld64.lld would previously allow you to link against dylibs linked with `-allowable_client`, even if the client's name does not match any allowed client. This change fixes that. See #114146 for related discussion. The test binary `liballowable_client.dylib` was created on macOS with: echo | clang -xc - -dynamiclib -mmacosx-version-min=10.11 -arch x86_64 -Wl,-allowable_client,allowed -o lib/liballowable_client.dylib
1 parent fe33bd0 commit 1de9bc1

File tree

8 files changed

+120
-2
lines changed

8 files changed

+120
-2
lines changed

lld/MachO/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ struct Configuration {
164164
llvm::StringRef finalOutput;
165165

166166
llvm::StringRef installName;
167+
llvm::StringRef clientName;
167168
llvm::StringRef mapFile;
168169
llvm::StringRef ltoObjPath;
169170
llvm::StringRef thinLTOJobs;

lld/MachO/Driver.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,6 +1881,15 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
18811881
config->installName = config->finalOutput;
18821882
}
18831883

1884+
auto getClientName = [&]() {
1885+
StringRef cn = path::filename(config->finalOutput);
1886+
cn.consume_front("lib");
1887+
auto firstDotOrUnderscore = cn.find_first_of("._");
1888+
cn = cn.take_front(firstDotOrUnderscore);
1889+
return cn;
1890+
};
1891+
config->clientName = args.getLastArgValue(OPT_client_name, getClientName());
1892+
18841893
if (args.hasArg(OPT_mark_dead_strippable_dylib)) {
18851894
if (config->outputType != MH_DYLIB)
18861895
warn("-mark_dead_strippable_dylib: ignored, only has effect with -dylib");

lld/MachO/DriverUtils.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,26 @@ DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella,
268268
if (newFile->exportingFile)
269269
newFile->parseLoadCommands(mbref);
270270
}
271+
272+
if (explicitlyLinked && !newFile->allowableClients.empty()) {
273+
bool allowed = std::any_of(
274+
newFile->allowableClients.begin(), newFile->allowableClients.end(),
275+
[&](StringRef allowableClient) {
276+
// We only do a prefix match to match LD64's behaviour.
277+
return allowableClient.starts_with(config->clientName);
278+
});
279+
280+
// TODO: This behaviour doesn't quite match the latest available source
281+
// release of LD64 (ld64-951.9), which allows "parents" and "siblings"
282+
// to link to libraries even when they're not explicitly named as
283+
// allowable clients. However, behaviour around this seems to have
284+
// changed in the latest release of Xcode (ld64-1115.7.3), so it's not
285+
// clear what the correct thing to do is yet.
286+
if (!allowed)
287+
error("cannot link directly with '" +
288+
sys::path::filename(newFile->installName) + "' because " +
289+
config->clientName + " is not an allowed client");
290+
}
271291
return newFile;
272292
}
273293

lld/MachO/InputFiles.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,14 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
17301730
? this
17311731
: this->umbrella;
17321732

1733+
if (!canBeImplicitlyLinked) {
1734+
for (auto *cmd : findCommands<sub_client_command>(hdr, LC_SUB_CLIENT)) {
1735+
StringRef allowableClient{reinterpret_cast<const char *>(cmd) +
1736+
cmd->client};
1737+
allowableClients.push_back(allowableClient);
1738+
}
1739+
}
1740+
17331741
const auto *dyldInfo = findCommand<dyld_info_command>(hdr, LC_DYLD_INFO_ONLY);
17341742
const auto *exportsTrie =
17351743
findCommand<linkedit_data_command>(hdr, LC_DYLD_EXPORTS_TRIE);
@@ -1891,6 +1899,12 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella,
18911899
exportingFile = (canBeImplicitlyLinked && isImplicitlyLinked(installName))
18921900
? this
18931901
: umbrella;
1902+
1903+
if (!canBeImplicitlyLinked)
1904+
for (const auto &allowableClient : interface.allowableClients())
1905+
allowableClients.push_back(
1906+
*make<std::string>(allowableClient.getInstallName().data()));
1907+
18941908
auto addSymbol = [&](const llvm::MachO::Symbol &symbol,
18951909
const Twine &name) -> void {
18961910
StringRef savedName = saver().save(name);

lld/MachO/InputFiles.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ class DylibFile final : public InputFile {
241241
DylibFile *exportingFile = nullptr;
242242
DylibFile *umbrella;
243243
SmallVector<StringRef, 2> rpaths;
244+
SmallVector<StringRef> allowableClients;
244245
uint32_t compatibilityVersion = 0;
245246
uint32_t currentVersion = 0;
246247
int64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel

lld/MachO/Options.td

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -875,8 +875,7 @@ def allowable_client : Separate<["-"], "allowable_client">,
875875
Group<grp_rare>;
876876
def client_name : Separate<["-"], "client_name">,
877877
MetaVarName<"<name>">,
878-
HelpText<"Specifies a <name> this client should match with the -allowable_client <name> in a dependent dylib">,
879-
Flags<[HelpHidden]>,
878+
HelpText<"Specifies a <name> this client should match with the -allowable_client <name> in an explicitly linked dylib">,
880879
Group<grp_rare>;
881880
def umbrella : Separate<["-"], "umbrella">,
882881
MetaVarName<"<name>">,
Binary file not shown.

lld/test/MachO/allowable-client.s

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# REQUIRES: x86
2+
# RUN: rm -rf %t; split-file %s %t
3+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
4+
5+
# Check linking against a .dylib
6+
# RUN: not %lld -o %t/test %t/test.o -L%S/Inputs -lallowable_client 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-IMPLICIT
7+
# RUN: not %lld -o %t/libtest_debug.exe %t/test.o -L%S/Inputs -lallowable_client 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-IMPLICIT
8+
# 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
9+
# RUN: %lld -o %t/test %t/test.o -L%S/Inputs -lallowable_client -client_name allowed
10+
# RUN: %lld -o %t/test %t/test.o -L%S/Inputs -lallowable_client -client_name all
11+
# RUN: %lld -o %t/all %t/test.o -L%S/Inputs -lallowable_client
12+
# RUN: %lld -o %t/allowed %t/test.o -L%S/Inputs -lallowable_client
13+
# RUN: %lld -o %t/liballowed_debug.exe %t/test.o -L%S/Inputs -lallowable_client
14+
15+
# Check linking against a .tbd
16+
# RUN: not %lld -o %t/test %t/test.o -L%t -lallowable_client 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-IMPLICIT
17+
# RUN: not %lld -o %t/libtest_debug.exe %t/test.o -L%t -lallowable_client 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-IMPLICIT
18+
# RUN: not %lld -o %t/test %t/test.o -L%t -lallowable_client -client_name notallowed 2>&1 | FileCheck %s --check-prefix=NOTALLOWED-EXPLICIT
19+
# RUN: %lld -o %t/test %t/test.o -L%t -lallowable_client -client_name allowed
20+
# RUN: %lld -o %t/test %t/test.o -L%t -lallowable_client -client_name all
21+
# RUN: %lld -o %t/all %t/test.o -L%t -lallowable_client
22+
# RUN: %lld -o %t/allowed %t/test.o -L%t -lallowable_client
23+
# RUN: %lld -o %t/liballowed_debug.exe %t/test.o -L%t -lallowable_client
24+
25+
# NOTALLOWED-IMPLICIT: error: cannot link directly with 'liballowable_client.dylib' because test is not an allowed client
26+
# NOTALLOWED-EXPLICIT: error: cannot link directly with 'liballowable_client.dylib' because notallowed is not an allowed client
27+
28+
#--- test.s
29+
.text
30+
.globl _main
31+
_main:
32+
ret
33+
34+
#--- liballowable_client.tbd
35+
{
36+
"main_library": {
37+
"allowable_clients": [
38+
{
39+
"clients": [
40+
"allowed"
41+
]
42+
}
43+
],
44+
"compatibility_versions": [
45+
{
46+
"version": "0"
47+
}
48+
],
49+
"current_versions": [
50+
{
51+
"version": "0"
52+
}
53+
],
54+
"flags": [
55+
{
56+
"attributes": [
57+
"not_app_extension_safe"
58+
]
59+
}
60+
],
61+
"install_names": [
62+
{
63+
"name": "lib/liballowable_client.dylib"
64+
}
65+
],
66+
"target_info": [
67+
{
68+
"min_deployment": "10.11",
69+
"target": "x86_64-macos"
70+
}
71+
]
72+
},
73+
"tapi_tbd_version": 5
74+
}

0 commit comments

Comments
 (0)