Skip to content

Commit 484c961

Browse files
Kyungwoo Leekyulee-com
Kyungwoo Lee
authored andcommitted
[lld-macho] Postprocess LC Linker Option
LLD resolves symbols regardless of LTO modes early when reading and parsing input files in order. The object files built from LTO passes are appended later. Because LLD eagerly resolves the LC linker options while parsing a new object file (and its chain of dependent libraries), the prior decision on pending prevailing symbols (belonging to some bitcode files) can change to ones in those native libraries that are just loaded. This patch delays processing LC linker options until all the native object files are added after LTO is done, similar to LD64. This way we preserve the decision on prevailing symbols LLD made, regardless of LTO modes. - When parsing a new object file in `parseLinkerOptions()`, it just parses LC linker options in the header, and saves those contents to `unprocessedLCLinkerOptions`. - After LTO is finished, `resolveLCLinkerOptions()` is called to recursively load dependent libraries, starting with initial linker options collected in `unprocessedLCLinkerOptions` (which also updates during recursions) Reviewed By: #lld-macho, int3 Differential Revision: https://reviews.llvm.org/D157716
1 parent 9032058 commit 484c961

File tree

7 files changed

+293
-25
lines changed

7 files changed

+293
-25
lines changed

lld/MachO/Driver.cpp

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ static InputFile *addFile(StringRef path, LoadType loadType,
411411
static std::vector<StringRef> missingAutolinkWarnings;
412412
static void addLibrary(StringRef name, bool isNeeded, bool isWeak,
413413
bool isReexport, bool isHidden, bool isExplicit,
414-
LoadType loadType, InputFile *originFile = nullptr) {
414+
LoadType loadType) {
415415
if (std::optional<StringRef> path = findLibrary(name)) {
416416
if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
417417
addFile(*path, loadType, /*isLazy=*/false, isExplicit,
@@ -428,19 +428,16 @@ static void addLibrary(StringRef name, bool isNeeded, bool isWeak,
428428
return;
429429
}
430430
if (loadType == LoadType::LCLinkerOption) {
431-
assert(originFile);
432431
missingAutolinkWarnings.push_back(
433-
saver().save(toString(originFile) +
434-
": auto-linked library not found for -l" + name));
432+
saver().save("auto-linked library not found for -l" + name));
435433
return;
436434
}
437435
error("library not found for -l" + name);
438436
}
439437

440438
static DenseSet<StringRef> loadedObjectFrameworks;
441439
static void addFramework(StringRef name, bool isNeeded, bool isWeak,
442-
bool isReexport, bool isExplicit, LoadType loadType,
443-
InputFile *originFile = nullptr) {
440+
bool isReexport, bool isExplicit, LoadType loadType) {
444441
if (std::optional<StringRef> path = findFramework(name)) {
445442
if (loadedObjectFrameworks.contains(*path))
446443
return;
@@ -468,10 +465,8 @@ static void addFramework(StringRef name, bool isNeeded, bool isWeak,
468465
return;
469466
}
470467
if (loadType == LoadType::LCLinkerOption) {
471-
assert(originFile);
472-
missingAutolinkWarnings.push_back(saver().save(
473-
toString(originFile) +
474-
": auto-linked framework not found for -framework " + name));
468+
missingAutolinkWarnings.push_back(
469+
saver().save("auto-linked framework not found for -framework " + name));
475470
return;
476471
}
477472
error("framework not found for -framework " + name);
@@ -480,7 +475,9 @@ static void addFramework(StringRef name, bool isNeeded, bool isWeak,
480475
// Parses LC_LINKER_OPTION contents, which can add additional command line
481476
// flags. This directly parses the flags instead of using the standard argument
482477
// parser to improve performance.
483-
void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) {
478+
void macho::parseLCLinkerOption(
479+
llvm::SmallVectorImpl<StringRef> &LCLinkerOptions, InputFile *f,
480+
unsigned argc, StringRef data) {
484481
if (config->ignoreAutoLink)
485482
return;
486483

@@ -498,19 +495,42 @@ void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) {
498495
if (arg.consume_front("-l")) {
499496
if (config->ignoreAutoLinkOptions.contains(arg))
500497
return;
501-
addLibrary(arg, /*isNeeded=*/false, /*isWeak=*/false,
502-
/*isReexport=*/false, /*isHidden=*/false, /*isExplicit=*/false,
503-
LoadType::LCLinkerOption, f);
504498
} else if (arg == "-framework") {
505499
StringRef name = argv[++i];
506500
if (config->ignoreAutoLinkOptions.contains(name))
507501
return;
508-
addFramework(name, /*isNeeded=*/false, /*isWeak=*/false,
509-
/*isReexport=*/false, /*isExplicit=*/false,
510-
LoadType::LCLinkerOption, f);
511502
} else {
512503
error(arg + " is not allowed in LC_LINKER_OPTION");
513504
}
505+
506+
LCLinkerOptions.append(argv);
507+
}
508+
509+
void macho::resolveLCLinkerOptions() {
510+
while (!unprocessedLCLinkerOptions.empty()) {
511+
SmallVector<StringRef> LCLinkerOptions(unprocessedLCLinkerOptions);
512+
unprocessedLCLinkerOptions.clear();
513+
514+
for (unsigned i = 0; i < LCLinkerOptions.size(); ++i) {
515+
StringRef arg = LCLinkerOptions[i];
516+
if (arg.consume_front("-l")) {
517+
if (config->ignoreAutoLinkOptions.contains(arg))
518+
continue;
519+
addLibrary(arg, /*isNeeded=*/false, /*isWeak=*/false,
520+
/*isReexport=*/false, /*isHidden=*/false,
521+
/*isExplicit=*/false, LoadType::LCLinkerOption);
522+
} else if (arg == "-framework") {
523+
StringRef name = LCLinkerOptions[++i];
524+
if (config->ignoreAutoLinkOptions.contains(name))
525+
continue;
526+
addFramework(name, /*isNeeded=*/false, /*isWeak=*/false,
527+
/*isReexport=*/false, /*isExplicit=*/false,
528+
LoadType::LCLinkerOption);
529+
} else {
530+
error(arg + " is not allowed in LC_LINKER_OPTION");
531+
}
532+
}
533+
}
514534
}
515535

516536
static void addFileList(StringRef path, bool isLazy) {
@@ -1387,6 +1407,7 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
13871407
missingAutolinkWarnings.clear();
13881408
syntheticSections.clear();
13891409
thunkMap.clear();
1410+
unprocessedLCLinkerOptions.clear();
13901411

13911412
firstTLVDataSection = nullptr;
13921413
tar = nullptr;
@@ -1889,6 +1910,8 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
18891910

18901911
bool didCompileBitcodeFiles = compileBitcodeFiles();
18911912

1913+
resolveLCLinkerOptions();
1914+
18921915
// If --thinlto-index-only is given, we should create only "index
18931916
// files" and not object files. Index file creation is already done
18941917
// in compileBitcodeFiles, so we are done if that's the case.

lld/MachO/Driver.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ enum {
4040
#undef OPTION
4141
};
4242

43-
void parseLCLinkerOption(InputFile *, unsigned argc, StringRef data);
43+
void parseLCLinkerOption(llvm::SmallVectorImpl<StringRef> &LCLinkerOptions,
44+
InputFile *f, unsigned argc, StringRef data);
45+
void resolveLCLinkerOptions();
4446

4547
std::string createResponseFile(const llvm::opt::InputArgList &args);
4648

lld/MachO/InputFiles.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,19 @@ OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName,
950950
section.subsections.push_back({0, isec});
951951
}
952952

953+
template <class LP>
954+
void ObjFile::parseLinkerOptions(SmallVectorImpl<StringRef> &LCLinkerOptions) {
955+
using Header = typename LP::mach_header;
956+
auto *hdr = reinterpret_cast<const Header *>(mb.getBufferStart());
957+
958+
for (auto *cmd : findCommands<linker_option_command>(hdr, LC_LINKER_OPTION)) {
959+
StringRef data{reinterpret_cast<const char *>(cmd + 1),
960+
cmd->cmdsize - sizeof(linker_option_command)};
961+
parseLCLinkerOption(LCLinkerOptions, this, cmd->count, data);
962+
}
963+
}
964+
965+
SmallVector<StringRef> macho::unprocessedLCLinkerOptions;
953966
ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
954967
bool lazy, bool forceHidden, bool compatArch)
955968
: InputFile(ObjKind, mb, lazy), modTime(modTime), forceHidden(forceHidden) {
@@ -983,11 +996,11 @@ template <class LP> void ObjFile::parse() {
983996
if (!(compatArch = compatWithTargetArch(this, hdr)))
984997
return;
985998

986-
for (auto *cmd : findCommands<linker_option_command>(hdr, LC_LINKER_OPTION)) {
987-
StringRef data{reinterpret_cast<const char *>(cmd + 1),
988-
cmd->cmdsize - sizeof(linker_option_command)};
989-
parseLCLinkerOption(this, cmd->count, data);
990-
}
999+
// We will resolve LC linker options once all native objects are loaded after
1000+
// LTO is finished.
1001+
SmallVector<StringRef, 4> LCLinkerOptions;
1002+
parseLinkerOptions<LP>(LCLinkerOptions);
1003+
unprocessedLCLinkerOptions.append(LCLinkerOptions);
9911004

9921005
ArrayRef<SectionHeader> sectionHeaders;
9931006
if (const load_command *cmd = findCommand(hdr, LP::segmentLCType)) {

lld/MachO/InputFiles.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ class ObjFile final : public InputFile {
164164
ArrayRef<llvm::MachO::data_in_code_entry> getDataInCode() const;
165165
ArrayRef<uint8_t> getOptimizationHints() const;
166166
template <class LP> void parse();
167+
template <class LP>
168+
void parseLinkerOptions(llvm::SmallVectorImpl<StringRef> &LinkerOptions);
167169

168170
static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
169171

@@ -317,6 +319,7 @@ class BitcodeFile final : public InputFile {
317319

318320
extern llvm::SetVector<InputFile *> inputFiles;
319321
extern llvm::DenseMap<llvm::CachedHashStringRef, MemoryBufferRef> cachedReads;
322+
extern llvm::SmallVector<StringRef> unprocessedLCLinkerOptions;
320323

321324
std::optional<MemoryBufferRef> readFile(StringRef path);
322325

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
; REQUIRES: x86
2+
; RUN: rm -rf %t; split-file %s %t
3+
4+
; RUN: llc -filetype=obj %t/q.ll -o %t/q.o
5+
; RUN: llvm-ar cru %t/libq.a %t/q.o
6+
7+
; RUN: llc -filetype=obj %t/f.ll -o %t/f.nolto.o
8+
; RUN: opt --thinlto-bc %t/f.ll -o %t/f.thinlto.o
9+
; RUN: opt %t/f.ll -o %t/f.lto.o
10+
11+
; RUN: llc -filetype=obj %t/b.ll -o %t/b.nolto.o
12+
; RUN: opt --thinlto-bc %t/b.ll -o %t/b.thinlto.o
13+
; RUN: opt %t/b.ll -o %t/b.lto.o
14+
15+
; (1) NoLTO-NoLTO
16+
; RUN: %lld -dylib -lSystem -L%t %t/f.nolto.o %t/b.nolto.o -o %t/nolto-nolto.out
17+
; RUN: llvm-objdump --syms %t/nolto-nolto.out | FileCheck %s
18+
19+
; (2) NoLTO-ThinLTO
20+
; RUN: %lld -dylib -lSystem -L%t %t/f.nolto.o %t/b.thinlto.o -o %t/nolto-thinlto.out
21+
; RUN: llvm-objdump --syms %t/nolto-thinlto.out | FileCheck %s
22+
23+
; (3) ThinLTO-NoLTO
24+
; RUN: %lld -dylib -lSystem -L%t %t/f.thinlto.o %t/b.nolto.o -o %t/thinlto-nolto.out
25+
; RUN: llvm-objdump --syms %t/thinlto-nolto.out | FileCheck %s
26+
27+
; (4) NoLTO-LTO
28+
; RUN: %lld -dylib -lSystem -L%t %t/f.nolto.o %t/b.lto.o -o %t/nolto-lto.out
29+
; RUN: llvm-objdump --syms %t/nolto-lto.out | FileCheck %s
30+
31+
; (5) LTO-NoLTO
32+
; RUN: %lld -dylib -lSystem -L%t %t/f.lto.o %t/b.nolto.o -o %t/lto-nolto.out
33+
; RUN: llvm-objdump --syms %t/lto-nolto.out | FileCheck %s
34+
35+
; (6) LTO-ThinLTO
36+
; RUN: %lld -dylib -lSystem -L%t %t/f.lto.o %t/b.thinlto.o -o %t/lto-thinlto.out
37+
; RUN: llvm-objdump --syms %t/lto-thinlto.out | FileCheck %s
38+
39+
; (7) ThinLTO-NoLTO
40+
; RUN: %lld -dylib -lSystem -L%t %t/f.thinlto.o %t/b.lto.o -o %t/thinlto-lto.out
41+
; RUN: llvm-objdump --syms %t/thinlto-lto.out | FileCheck %s
42+
43+
; We expect to resolve _weak1 from f.ll and _weak2 from b.ll as per the input order.
44+
; As _weak2 from q.ll pulled in via LC_LINKER_OPTION is processed
45+
; in the second pass, it won't prevail due to _weak2 from b.ll.
46+
47+
; CHECK: w O __TEXT,f _weak1
48+
; CHECK: w O __TEXT,b _weak2
49+
50+
;--- q.ll
51+
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
52+
target triple = "x86_64-apple-macosx10.15.0"
53+
54+
define i32 @weak2() section "__TEXT,q" {
55+
ret i32 2
56+
}
57+
58+
;--- f.ll
59+
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
60+
target triple = "x86_64-apple-macosx10.15.0"
61+
62+
!0 = !{!"-lq"}
63+
!llvm.linker.options = !{!0}
64+
65+
define weak i32 @weak1() section "__TEXT,f" {
66+
%call = call i32 @weak2()
67+
%add = add nsw i32 %call, 1
68+
ret i32 %add
69+
}
70+
71+
declare i32 @weak2(...)
72+
73+
;--- b.ll
74+
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
75+
target triple = "x86_64-apple-macosx10.15.0"
76+
77+
define weak i32 @weak1() section "__TEXT,b" {
78+
ret i32 3
79+
}
80+
81+
define weak i32 @weak2() section "__TEXT,b" {
82+
ret i32 4
83+
}

0 commit comments

Comments
 (0)