Skip to content

Commit 5ad2c22

Browse files
committed
Revert "Revert "[lld-macho] Implement -dependency_info (partially - more opcodes needed)""
This reverts commit 2554b95. Relanding [lld-macho] Implement -dependency_info (D98559) with changes: - inline functions removed from cpp file. - updated tests to not check libSystem.tbd with other input files (because of possible indeterministic ordering)
1 parent 9fdfd8d commit 5ad2c22

File tree

6 files changed

+191
-3
lines changed

6 files changed

+191
-3
lines changed

lld/MachO/Driver.cpp

+9-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ using namespace llvm::sys;
5454
using namespace lld;
5555
using namespace lld::macho;
5656

57-
Configuration *lld::macho::config;
57+
Configuration *macho::config;
58+
DependencyTracker *macho::depTracker;
5859

5960
static HeaderFileType getOutputType(const InputArgList &args) {
6061
// TODO: -r, -dylinker, -preload...
@@ -84,6 +85,8 @@ findAlongPathsWithExtensions(StringRef name, ArrayRef<StringRef> extensions) {
8485
Twine location = base + ext;
8586
if (fs::exists(location))
8687
return location.str();
88+
else
89+
depTracker->logFileNotFound(location);
8790
}
8891
}
8992
return {};
@@ -815,6 +818,9 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
815818
symtab = make<SymbolTable>();
816819
target = createTargetInfo(args);
817820

821+
depTracker =
822+
make<DependencyTracker>(args.getLastArgValue(OPT_dependency_info, ""));
823+
818824
config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"),
819825
/*file=*/nullptr,
820826
/*isWeakRef=*/false);
@@ -1066,6 +1072,8 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
10661072

10671073
// Write to an output file.
10681074
writeResult();
1075+
1076+
depTracker->write(getLLDVersion(), inputFiles, config->outputFile);
10691077
}
10701078

10711079
if (config->timeTraceEnabled) {

lld/MachO/Driver.h

+50
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,14 @@
1111

1212
#include "lld/Common/LLVM.h"
1313
#include "llvm/ADT/Optional.h"
14+
#include "llvm/ADT/SetVector.h"
1415
#include "llvm/ADT/StringRef.h"
1516
#include "llvm/Option/OptTable.h"
1617
#include "llvm/Support/MemoryBuffer.h"
1718

19+
#include <set>
20+
#include <type_traits>
21+
1822
namespace llvm {
1923
namespace MachO {
2024
class InterfaceFile;
@@ -61,6 +65,52 @@ uint32_t getModTime(llvm::StringRef path);
6165

6266
void printArchiveMemberLoad(StringRef reason, const InputFile *);
6367

68+
// Helper class to export dependency info.
69+
class DependencyTracker {
70+
public:
71+
explicit DependencyTracker(llvm::StringRef path);
72+
73+
// Adds the given path to the set of not-found files.
74+
inline void logFileNotFound(std::string path) {
75+
if (active)
76+
notFounds.insert(std::move(path));
77+
}
78+
79+
inline void logFileNotFound(const Twine &path) {
80+
if (active)
81+
notFounds.insert(path.str());
82+
}
83+
84+
// Writes the dependencies to specified path.
85+
// The content is sorted by its Op Code, then within each section,
86+
// alphabetical order.
87+
void write(llvm::StringRef version,
88+
const llvm::SetVector<InputFile *> &inputs,
89+
llvm::StringRef output);
90+
91+
private:
92+
enum DepOpCode : char {
93+
// Denotes the linker version.
94+
Version = 0x00,
95+
// Denotes the input files.
96+
Input = 0x10,
97+
// Denotes the files that do not exist(?)
98+
NotFound = 0x11,
99+
// Denotes the output files.
100+
Output = 0x40,
101+
};
102+
103+
const llvm::StringRef path;
104+
bool active;
105+
106+
// The paths need to be alphabetically ordered.
107+
// We need to own the paths because some of them are temporarily
108+
// constructed.
109+
std::set<std::string> notFounds;
110+
};
111+
112+
extern DependencyTracker *depTracker;
113+
64114
} // namespace macho
65115
} // namespace lld
66116

lld/MachO/DriverUtils.cpp

+51-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/Option/ArgList.h"
2424
#include "llvm/Option/Option.h"
2525
#include "llvm/Support/CommandLine.h"
26+
#include "llvm/Support/FileSystem.h"
2627
#include "llvm/Support/Path.h"
2728
#include "llvm/TextAPI/MachO/InterfaceFile.h"
2829
#include "llvm/TextAPI/MachO/TextAPIReader.h"
@@ -164,12 +165,15 @@ Optional<std::string> macho::resolveDylibPath(StringRef path) {
164165
// they are consistent.
165166
if (fs::exists(path))
166167
return std::string(path);
168+
else
169+
depTracker->logFileNotFound(path);
167170

168171
SmallString<261> location = path;
169172
path::replace_extension(location, ".tbd");
170173
if (fs::exists(location))
171174
return std::string(location);
172-
175+
else
176+
depTracker->logFileNotFound(location);
173177
return {};
174178
}
175179

@@ -240,3 +244,49 @@ void macho::printArchiveMemberLoad(StringRef reason, const InputFile *f) {
240244
if (config->printWhyLoad)
241245
message(reason + " forced load of " + toString(f));
242246
}
247+
248+
macho::DependencyTracker::DependencyTracker(StringRef path)
249+
: path(path), active(!path.empty()) {
250+
if (active && fs::exists(path) && !fs::can_write(path)) {
251+
warn("Ignoring dependency_info option since specified path is not "
252+
"writeable.");
253+
active = false;
254+
}
255+
}
256+
257+
void macho::DependencyTracker::write(llvm::StringRef version,
258+
const llvm::SetVector<InputFile *> &inputs,
259+
llvm::StringRef output) {
260+
if (!active)
261+
return;
262+
263+
std::error_code ec;
264+
llvm::raw_fd_ostream os(path, ec, llvm::sys::fs::OF_None);
265+
if (ec) {
266+
warn("Error writing dependency info to file");
267+
return;
268+
}
269+
270+
auto addDep = [&os](DepOpCode opcode, const StringRef &path) {
271+
os << opcode;
272+
os << path;
273+
os << '\0';
274+
};
275+
276+
addDep(DepOpCode::Version, version);
277+
278+
// Sort the input by its names.
279+
std::vector<StringRef> inputNames;
280+
inputNames.reserve(inputs.size());
281+
for (InputFile *f : inputs)
282+
inputNames.push_back(f->getName());
283+
llvm::sort(inputNames,
284+
[](const StringRef &a, const StringRef &b) { return a < b; });
285+
for (const StringRef &in : inputNames)
286+
addDep(DepOpCode::Input, in);
287+
288+
for (const std::string &f : notFounds)
289+
addDep(DepOpCode::NotFound, f);
290+
291+
addDep(DepOpCode::Output, output);
292+
}

lld/MachO/Options.td

-1
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,6 @@ def map : Separate<["-"], "map">,
504504
def dependency_info : Separate<["-"], "dependency_info">,
505505
MetaVarName<"<path>">,
506506
HelpText<"Dump dependency info">,
507-
Flags<[HelpHidden]>,
508507
Group<grp_introspect>;
509508
def save_temps : Flag<["-"], "save-temps">,
510509
HelpText<"Save intermediate LTO compilation results">,
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#
2+
# Dump the dependency file (produced with -dependency_info) to text
3+
# format for testing purposes.
4+
#
5+
6+
import sys
7+
8+
f = open(sys.argv[1], "rb")
9+
byte = f.read(1)
10+
while byte != b'':
11+
if byte == b'\x00':
12+
sys.stdout.write("lld-version: ")
13+
elif byte == b'\x10':
14+
sys.stdout.write("input-file: ")
15+
elif byte == b'\x11':
16+
sys.stdout.write("not-found: ")
17+
elif byte == b'\x40':
18+
sys.stdout.write("output-file: ")
19+
byte = f.read(1)
20+
while byte != b'\x00':
21+
sys.stdout.write(byte.decode("ascii"))
22+
byte = f.read(1)
23+
sys.stdout.write("\n")
24+
byte = f.read(1)
25+
26+
f.close()

lld/test/MachO/dependency-info.s

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# REQUIRES: x86
2+
## FIXME: Paths on windows have both `\` and '/', as a result, they are in a different
3+
## order when sorted. Maybe create a separate test for that?
4+
# UNSUPPORTED: system-windows
5+
#
6+
# RUN: rm -rf %t
7+
# RUN: split-file %s %t
8+
9+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/foo.o %t/foo.s
10+
# RUN: %lld -dylib -o %t/libfoo.dylib %t/foo.o
11+
12+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/bar.o %t/bar.s
13+
# RUN: llvm-ar csr %t/bar.a %t/bar.o
14+
15+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/main.o %t/main.s
16+
17+
# RUN: %lld %t/main.o %t/bar.a %t/libfoo.dylib -lSystem -dependency_info %t/deps_info.out
18+
19+
## Smoke check to make sure that `libSystem.tbd` exists in the set of input.
20+
# RUN: %python %S/Inputs/DependencyDump.py %t/deps_info.out | FileCheck %s --check-prefix "SYS-CHECK"
21+
# SYS-CHECK: input-file: {{.*}}/libSystem.tbd
22+
23+
## Filter out libSystem.tbd because it is in the source tree, which is different from the rest,
24+
## therefore ordering might be unpredictable.
25+
# RUN: %python %S/Inputs/DependencyDump.py %t/deps_info.out | grep -v 'libSystem.tbd' | FileCheck %s
26+
27+
# CHECK: lld-version: LLD {{.*}}
28+
29+
# CHECK-NEXT: input-file: {{.*}}/bar.a
30+
# CHECK-NEXT: input-file: {{.*}}/libfoo.dylib
31+
# CHECK-NEXT: input-file: {{.*}}/main.o
32+
# CHECK-NEXT: bar.o
33+
34+
# CHECK-NEXT: not-found: {{.*}}/libdyld.dylib
35+
## There could be more not-found here but we are not checking those because it's brittle.
36+
37+
# CHECK: output-file: a.out
38+
39+
#--- foo.s
40+
.globl __Z3foo
41+
__Z3foo:
42+
ret
43+
44+
#--- bar.s
45+
.globl _bar
46+
_bar:
47+
callq __Z3foo
48+
ret
49+
50+
#--- main.s
51+
.globl _main
52+
_main:
53+
callq _bar
54+
callq __Z3foo
55+
ret

0 commit comments

Comments
 (0)