1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use lddtree::DependencyAnalyzer;
#[test]
fn test_elf() {
let analyzer = DependencyAnalyzer::default();
let deps = analyzer.analyze("tests/test.elf").unwrap();
assert_eq!(
deps.interpreter.as_deref(),
Some("/lib/ld-linux-aarch64.so.1")
);
assert_eq!(
deps.needed,
&[
"libz.so.1",
"libpthread.so.0",
"libm.so.6",
"libdl.so.2",
"libc.so.6",
]
);
// All directly needed libraries must appear in the dependency map
for name in &deps.needed {
assert!(
deps.libraries.contains_key(name.as_str()),
"missing library: {name}"
);
}
// The interpreter is keyed by its soname (basename), not its full path.
// Keying by full path duplicated the dynamic loader on aarch64 glibc
// hosts, where it is also reachable via DT_NEEDED (issue #19).
assert!(deps.libraries.contains_key("ld-linux-aarch64.so.1"));
assert!(!deps.libraries.contains_key("/lib/ld-linux-aarch64.so.1"));
// 5 direct deps + the interpreter. On aarch64 glibc hosts transitive
// deps are found on disk, but they resolve to the same 6 names.
assert_eq!(deps.libraries.len(), 6);
}
#[test]
fn test_macho() {
let analyzer = DependencyAnalyzer::default();
let deps = analyzer.analyze("tests/test.macho").unwrap();
assert!(deps.interpreter.is_none());
assert_eq!(
deps.needed,
&[
"/usr/lib/libz.1.dylib",
"/usr/lib/libiconv.2.dylib",
"/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
"/usr/lib/libSystem.B.dylib"
]
);
// On macOS, these system libraries exist on disk (in the dyld shared cache),
// so transitive dependencies will be discovered, making the count >= 4.
// On other platforms, the install-name paths don't exist, so we get exactly 4
// not-found entries.
assert!(deps.libraries.len() >= 4);
}
#[test]
fn test_pe() {
let analyzer = DependencyAnalyzer::default();
let deps = analyzer.analyze("tests/test.pe").unwrap();
assert!(deps.interpreter.is_none());
assert_eq!(
deps.needed,
&[
"KERNEL32.dll",
"VCRUNTIME140.dll",
"api-ms-win-crt-runtime-l1-1-0.dll",
"api-ms-win-crt-stdio-l1-1-0.dll"
]
);
// All directly needed libraries must appear in the dependency map
for name in &deps.needed {
assert!(
deps.libraries.contains_key(name.as_str()),
"missing library: {name}"
);
}
// API set DLLs are virtual — they never exist as real files on disk
assert!(!deps.libraries["api-ms-win-crt-runtime-l1-1-0.dll"].found());
assert!(!deps.libraries["api-ms-win-crt-stdio-l1-1-0.dll"].found());
// On Windows, real system DLLs (e.g., KERNEL32.dll) are found on disk and
// their transitive dependencies are discovered, so the total library count
// exceeds the 4 direct deps. On Linux/macOS no Windows system directories
// exist, so all non-API-set libs are recorded as not-found and the count
// stays at 4.
assert!(deps.libraries.len() >= 4);
}