Skip to content

Commit 47b63cd

Browse files
authored
[lld-macho] Save all thin archive members in repro tarball (#97169)
Previously, we only saved those members of thin archives into a repro file that were actually used during linking. However, -ObjC handling requires us to inspect all members, even those that don't end up being loaded. We weren't handling missing members correctly and crashed with an "unhandled `Error`" failure in LLVM_ENABLE_ABI_BREAKING_CHECKS builds. To fix this, we now eagerly load all object files and warn when encountering missing members (in the instances where it wasn't a hard error before). To avoid having to patch out the checks when dealing with older repro files, the `--no-warn-thin-archive-missing-members` flag is added as an escape hatch.
1 parent 1cc1072 commit 47b63cd

File tree

6 files changed

+83
-12
lines changed

6 files changed

+83
-12
lines changed

lld/MachO/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ struct Configuration {
212212
bool csProfileGenerate = false;
213213
llvm::StringRef csProfilePath;
214214
bool pgoWarnMismatch;
215+
bool warnThinArchiveMissingMembers;
215216

216217
bool callGraphProfileSort = false;
217218
llvm::StringRef printSymbolOrder;

lld/MachO/Driver.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,20 @@ struct ArchiveFileInfo {
270270

271271
static DenseMap<StringRef, ArchiveFileInfo> loadedArchives;
272272

273+
static void saveThinArchiveToRepro(ArchiveFile const *file) {
274+
assert(tar && file->getArchive().isThin());
275+
276+
Error e = Error::success();
277+
for (const object::Archive::Child &c : file->getArchive().children(e)) {
278+
MemoryBufferRef mb = CHECK(c.getMemoryBufferRef(),
279+
toString(file) + ": failed to get buffer");
280+
tar->append(relativeToRoot(CHECK(c.getFullName(), file)), mb.getBuffer());
281+
}
282+
if (e)
283+
error(toString(file) +
284+
": Archive::children failed: " + toString(std::move(e)));
285+
}
286+
273287
static InputFile *addFile(StringRef path, LoadType loadType,
274288
bool isLazy = false, bool isExplicit = true,
275289
bool isBundleLoader = false,
@@ -301,6 +315,9 @@ static InputFile *addFile(StringRef path, LoadType loadType,
301315
if (!archive->isEmpty() && !archive->hasSymbolTable())
302316
error(path + ": archive has no index; run ranlib to add one");
303317
file = make<ArchiveFile>(std::move(archive), isForceHidden);
318+
319+
if (tar && file->getArchive().isThin())
320+
saveThinArchiveToRepro(file);
304321
} else {
305322
file = entry->second.file;
306323
// Command-line loads take precedence. If file is previously loaded via
@@ -330,9 +347,13 @@ static InputFile *addFile(StringRef path, LoadType loadType,
330347
reason = "-all_load";
331348
break;
332349
}
333-
if (Error e = file->fetch(c, reason))
334-
error(toString(file) + ": " + reason +
335-
" failed to load archive member: " + toString(std::move(e)));
350+
if (Error e = file->fetch(c, reason)) {
351+
if (config->warnThinArchiveMissingMembers)
352+
warn(toString(file) + ": " + reason +
353+
" failed to load archive member: " + toString(std::move(e)));
354+
else
355+
llvm::consumeError(std::move(e));
356+
}
336357
}
337358
if (e)
338359
error(toString(file) +
@@ -349,7 +370,18 @@ static InputFile *addFile(StringRef path, LoadType loadType,
349370
Error e = Error::success();
350371
for (const object::Archive::Child &c : file->getArchive().children(e)) {
351372
Expected<MemoryBufferRef> mb = c.getMemoryBufferRef();
352-
if (!mb || !hasObjCSection(*mb))
373+
if (!mb) {
374+
// We used to create broken repro tarballs that only included those
375+
// object files from thin archives that ended up being used.
376+
if (config->warnThinArchiveMissingMembers)
377+
warn(toString(file) + ": -ObjC failed to open archive member: " +
378+
toString(mb.takeError()));
379+
else
380+
llvm::consumeError(mb.takeError());
381+
continue;
382+
}
383+
384+
if (!hasObjCSection(*mb))
353385
continue;
354386
if (Error e = file->fetch(c, "-ObjC"))
355387
error(toString(file) + ": -ObjC failed to load archive member: " +
@@ -1699,6 +1731,9 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
16991731
config->csProfilePath = args.getLastArgValue(OPT_cs_profile_path);
17001732
config->pgoWarnMismatch =
17011733
args.hasFlag(OPT_pgo_warn_mismatch, OPT_no_pgo_warn_mismatch, true);
1734+
config->warnThinArchiveMissingMembers =
1735+
args.hasFlag(OPT_warn_thin_archive_missing_members,
1736+
OPT_no_warn_thin_archive_missing_members, true);
17021737
config->generateUuid = !args.hasArg(OPT_no_uuid);
17031738

17041739
for (const Arg *arg : args.filtered(OPT_alias)) {

lld/MachO/InputFiles.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,10 +2200,6 @@ Error ArchiveFile::fetch(const object::Archive::Child &c, StringRef reason) {
22002200
if (!mb)
22012201
return mb.takeError();
22022202

2203-
// Thin archives refer to .o files, so --reproduce needs the .o files too.
2204-
if (tar && c.getParent()->isThin())
2205-
tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb->getBuffer());
2206-
22072203
Expected<TimePoint<std::chrono::seconds>> modTime = c.getLastModified();
22082204
if (!modTime)
22092205
return modTime.takeError();

lld/MachO/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ def cs_profile_path: Joined<["--"], "cs-profile-path=">,
153153
defm pgo_warn_mismatch: BB<"pgo-warn-mismatch",
154154
"turn on warnings about profile cfg mismatch (default)",
155155
"turn off warnings about profile cfg mismatch">, Group<grp_lld>;
156+
defm warn_thin_archive_missing_members : BB<"warn-thin-archive-missing-members",
157+
"Warn on missing object files referenced by thin archives (default)",
158+
"Do not warn on missing object files referenced by thin archives">, Group<grp_lld>;
156159

157160
// This is a complete Options.td compiled from Apple's ld(1) manpage
158161
// dated 2018-03-07 and cross checked with ld64 source code in repo
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# REQUIRES: x86
2+
3+
## For a long time, LLD only included those members from thin archives that were actually used
4+
## during linking. However, we need to iterate over all members for -ObjC, check that we don't
5+
## crash when we encounter a missing member.
6+
7+
# RUN: rm -rf %t; mkdir %t
8+
# RUN: sed s/SYM/_main/ %s | llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/main.o
9+
# RUN: sed s/SYM/_unused/ %s | llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/unused.o
10+
11+
# RUN: cd %t; llvm-ar rcsT unused.a unused.o; rm unused.o
12+
## FIXME: Absolute paths don't end up relativized in the repro file.
13+
14+
# RUN: %no-fatal-warnings-lld %t/main.o %t/unused.a -ObjC -o /dev/null 2>&1 \
15+
# RUN: | FileCheck %s --check-prefix=WARN
16+
17+
# RUN: %lld %t/main.o %t/unused.a -ObjC --no-warn-thin-archive-missing-members -o /dev/null \
18+
# RUN: | FileCheck %s --implicit-check-not 'warning' --allow-empty
19+
20+
# WARN: ld64.lld: warning: {{.*}}unused.a: -ObjC failed to open archive member: 'unused.o'
21+
22+
.text
23+
.globl SYM
24+
SYM:
25+
ret
Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
# REQUIRES: x86
22

3-
# RUN: rm -rf %t.dir
4-
# RUN: mkdir -p %t.dir
5-
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %s -o %t.dir/foo.o
3+
# RUN: rm -rf %t.dir; split-file %s %t.dir
4+
5+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t.dir/foo.s -o %t.dir/foo.o
6+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t.dir/unused.s -o %t.dir/unused.o
67
# RUN: cd %t.dir
7-
# RUN: llvm-ar rcsT foo.a foo.o
8+
# RUN: llvm-ar rcsT foo.a foo.o unused.o
89

910
# RUN: %lld foo.a -o /dev/null --reproduce repro.tar
1011
# RUN: tar tf repro.tar | FileCheck -DPATH='repro/%:t.dir' %s
1112

1213
# RUN: %lld -all_load foo.a -o /dev/null --reproduce repro2.tar
1314
# RUN: tar tf repro2.tar | FileCheck -DPATH='repro2/%:t.dir' %s
1415

16+
# RUN: %lld -ObjC foo.a -o /dev/null --reproduce repro3.tar
17+
# RUN: tar tf repro3.tar | FileCheck -DPATH='repro3/%:t.dir' %s
18+
1519
# CHECK-DAG: [[PATH]]/foo.a
1620
# CHECK-DAG: [[PATH]]/foo.o
21+
# CHECK-DAG: [[PATH]]/unused.o
1722

23+
#--- foo.s
1824
.globl _main
1925
_main:
2026
nop
27+
28+
#--- unused.s
29+
.globl _unused
30+
_unused:
31+
nop

0 commit comments

Comments
 (0)