Skip to content

Commit 8bea91f

Browse files
[lld-macho] Support archives without index (#132942)
This is a ~port of https://reviews.llvm.org/D117284. Like in that change, archives without indices are treated as a collection of lazy object files (as in `--start-lib/--end-lib`) Porting the ELF follow-up to convert *all* archives to the lazy object code path (https://reviews.llvm.org/D119074) is a natural next step, but we would need to ensure the assertions about memory use hold for Mach-O. NB: without an index, we can't do the part of the `-ObjC` scan where we check for Objective-C symbols directly. We *can* still check for `__obcj` sections so I wonder how much of a problem this actually is, since I'm not sure how the "symbols but no sections" case can appear in the wild.
1 parent 589e1c7 commit 8bea91f

File tree

6 files changed

+117
-55
lines changed

6 files changed

+117
-55
lines changed

lld/MachO/Driver.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,6 @@ static InputFile *addFile(StringRef path, LoadType loadType,
314314
std::unique_ptr<object::Archive> archive = CHECK(
315315
object::Archive::create(mbref), path + ": failed to parse archive");
316316

317-
if (!archive->isEmpty() && !archive->hasSymbolTable())
318-
error(path + ": archive has no index; run ranlib to add one");
319317
file = make<ArchiveFile>(std::move(archive), isForceHidden);
320318

321319
if (tar && file->getArchive().isThin())
@@ -362,9 +360,11 @@ static InputFile *addFile(StringRef path, LoadType loadType,
362360
": Archive::children failed: " + toString(std::move(e)));
363361
}
364362
} else if (isCommandLineLoad && config->forceLoadObjC) {
365-
for (const object::Archive::Symbol &sym : file->getArchive().symbols())
366-
if (sym.getName().starts_with(objc::symbol_names::klass))
367-
file->fetch(sym);
363+
if (file->getArchive().hasSymbolTable()) {
364+
for (const object::Archive::Symbol &sym : file->getArchive().symbols())
365+
if (sym.getName().starts_with(objc::symbol_names::klass))
366+
file->fetch(sym);
367+
}
368368

369369
// TODO: no need to look for ObjC sections for a given archive member if
370370
// we already found that it contains an ObjC symbol.
@@ -394,7 +394,6 @@ static InputFile *addFile(StringRef path, LoadType loadType,
394394
": Archive::children failed: " + toString(std::move(e)));
395395
}
396396
}
397-
398397
file->addLazySymbols();
399398
loadedArchives[path] = ArchiveFileInfo{file, isCommandLineLoad};
400399
newFile = file;

lld/MachO/InputFiles.cpp

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,9 +2159,31 @@ ArchiveFile::ArchiveFile(std::unique_ptr<object::Archive> &&f, bool forceHidden)
21592159
void ArchiveFile::addLazySymbols() {
21602160
// Avoid calling getMemoryBufferRef() on zero-symbol archive
21612161
// since that crashes.
2162-
if (file->isEmpty() || file->getNumberOfSymbols() == 0)
2162+
if (file->isEmpty() ||
2163+
(file->hasSymbolTable() && file->getNumberOfSymbols() == 0))
21632164
return;
21642165

2166+
if (!file->hasSymbolTable()) {
2167+
// No index, treat each child as a lazy object file.
2168+
Error e = Error::success();
2169+
for (const object::Archive::Child &c : file->children(e)) {
2170+
// Check `seen` but don't insert so a future eager load can still happen.
2171+
if (seen.contains(c.getChildOffset()))
2172+
continue;
2173+
if (!seenLazy.insert(c.getChildOffset()).second)
2174+
continue;
2175+
auto file = childToObjectFile(c, /*lazy=*/true);
2176+
if (!file)
2177+
error(toString(this) +
2178+
": couldn't process child: " + toString(file.takeError()));
2179+
inputFiles.insert(*file);
2180+
}
2181+
if (e)
2182+
error(toString(this) +
2183+
": Archive::children failed: " + toString(std::move(e)));
2184+
return;
2185+
}
2186+
21652187
Error err = Error::success();
21662188
auto child = file->child_begin(err);
21672189
// Ignore the I/O error here - will be reported later.
@@ -2191,16 +2213,17 @@ void ArchiveFile::addLazySymbols() {
21912213

21922214
static Expected<InputFile *>
21932215
loadArchiveMember(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
2194-
uint64_t offsetInArchive, bool forceHidden, bool compatArch) {
2216+
uint64_t offsetInArchive, bool forceHidden, bool compatArch,
2217+
bool lazy) {
21952218
if (config->zeroModTime)
21962219
modTime = 0;
21972220

21982221
switch (identify_magic(mb.getBuffer())) {
21992222
case file_magic::macho_object:
2200-
return make<ObjFile>(mb, modTime, archiveName, /*lazy=*/false, forceHidden,
2223+
return make<ObjFile>(mb, modTime, archiveName, lazy, forceHidden,
22012224
compatArch);
22022225
case file_magic::bitcode:
2203-
return make<BitcodeFile>(mb, archiveName, offsetInArchive, /*lazy=*/false,
2226+
return make<BitcodeFile>(mb, archiveName, offsetInArchive, lazy,
22042227
forceHidden, compatArch);
22052228
default:
22062229
return createStringError(inconvertibleErrorCode(),
@@ -2212,19 +2235,7 @@ loadArchiveMember(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
22122235
Error ArchiveFile::fetch(const object::Archive::Child &c, StringRef reason) {
22132236
if (!seen.insert(c.getChildOffset()).second)
22142237
return Error::success();
2215-
2216-
Expected<MemoryBufferRef> mb = c.getMemoryBufferRef();
2217-
if (!mb)
2218-
return mb.takeError();
2219-
2220-
Expected<TimePoint<std::chrono::seconds>> modTime = c.getLastModified();
2221-
if (!modTime)
2222-
return modTime.takeError();
2223-
2224-
Expected<InputFile *> file =
2225-
loadArchiveMember(*mb, toTimeT(*modTime), getName(), c.getChildOffset(),
2226-
forceHidden, compatArch);
2227-
2238+
auto file = childToObjectFile(c, /*lazy=*/false);
22282239
if (!file)
22292240
return file.takeError();
22302241

@@ -2251,6 +2262,21 @@ void ArchiveFile::fetch(const object::Archive::Symbol &sym) {
22512262
toMachOString(symCopy) + ": " + toString(std::move(e)));
22522263
}
22532264

2265+
Expected<InputFile *>
2266+
ArchiveFile::childToObjectFile(const llvm::object::Archive::Child &c,
2267+
bool lazy) {
2268+
Expected<MemoryBufferRef> mb = c.getMemoryBufferRef();
2269+
if (!mb)
2270+
return mb.takeError();
2271+
2272+
Expected<TimePoint<std::chrono::seconds>> modTime = c.getLastModified();
2273+
if (!modTime)
2274+
return modTime.takeError();
2275+
2276+
return loadArchiveMember(*mb, toTimeT(*modTime), getName(),
2277+
c.getChildOffset(), forceHidden, compatArch, lazy);
2278+
}
2279+
22542280
static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym,
22552281
BitcodeFile &file) {
22562282
StringRef name = saver().save(objSym.getName());

lld/MachO/InputFiles.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,13 @@ class ArchiveFile final : public InputFile {
297297
static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
298298

299299
private:
300+
Expected<InputFile *> childToObjectFile(const llvm::object::Archive::Child &c,
301+
bool lazy);
300302
std::unique_ptr<llvm::object::Archive> file;
301303
// Keep track of children fetched from the archive by tracking
302304
// which address offsets have been fetched already.
303305
llvm::DenseSet<uint64_t> seen;
306+
llvm::DenseSet<uint64_t> seenLazy;
304307
// Load all symbols with hidden visibility (-load_hidden).
305308
bool forceHidden;
306309
};

lld/test/MachO/archive-no-index.ll

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
; REQUIRES: x86
2+
; RUN: rm -rf %t; split-file %s %t
3+
4+
; RUN: llvm-as %t/lib.ll -o %t/lib.o
5+
; RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/main.o %t/main.s
6+
; RUN: llvm-ar rcST %t/lib.a %t/lib.o
7+
; RUN: %lld %t/main.o %t/lib.a -o %t/out
8+
9+
;--- main.s
10+
.global _main
11+
_main:
12+
call _foo
13+
mov $0, %rax
14+
ret
15+
16+
;--- lib.ll
17+
target triple = "x86_64-apple-darwin"
18+
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
19+
20+
define void @foo() {
21+
entry:
22+
ret void
23+
}

lld/test/MachO/archive-no-index.s

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# REQUIRES: x86
2+
3+
# RUN: rm -rf %t; split-file %s %t
4+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/main.o %t/main.s
5+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/lib.o %t/lib.s
6+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/lib2.o %t/lib2.s
7+
# RUN: llvm-ar crST %t/lib.a %t/lib.o %t/lib2.o
8+
# RUN: %lld %t/main.o %t/lib.a -o %t/out
9+
# RUN: llvm-nm %t/out | FileCheck %s
10+
11+
# CHECK-NOT: T _bar
12+
# CHECK: T _foo
13+
14+
## Test that every kind of eager load mechanism still works.
15+
# RUN: %lld %t/main.o %t/lib.a -all_load -o %t/all_load
16+
# RUN: llvm-nm %t/all_load | FileCheck %s --check-prefix FORCED-LOAD
17+
# RUN: %lld %t/main.o -force_load %t/lib.a -o %t/force_load
18+
# RUN: llvm-nm %t/force_load | FileCheck %s --check-prefix FORCED-LOAD
19+
# RUN: %lld %t/main.o %t/lib.a -ObjC -o %t/objc
20+
# RUN: llvm-nm %t/objc | FileCheck %s --check-prefix FORCED-LOAD
21+
22+
# FORCED-LOAD: T _bar
23+
24+
#--- lib.s
25+
.global _foo
26+
_foo:
27+
ret
28+
29+
#--- lib2.s
30+
.section __DATA,__objc_catlist
31+
.quad 0x1234
32+
33+
.section __TEXT,__text
34+
.global _bar
35+
_bar:
36+
ret
37+
38+
#--- main.s
39+
.global _main
40+
_main:
41+
call _foo
42+
mov $0, %rax
43+
ret

lld/test/MachO/invalid/archive-no-index.s

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)