GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 67.8% 40 / 15 / 74
Functions: 100.0% 7 / 0 / 7
Branches: 35.3% 41 / 60 / 176

src/linker/ExternalLinkerInterface.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "ExternalLinkerInterface.h"
4
5 #include <iostream>
6
7 #include <driver/Driver.h>
8 #include <exception/CompilerError.h>
9 #include <exception/LinkerError.h>
10 #include <util/SystemUtil.h>
11 #include <util/Timer.h>
12
13 namespace spice::compiler {
14
15 443 ExternalLinkerInterface::ExternalLinkerInterface(const CliOptions &cliOptions)
16 443 : outputPath(cliOptions.outputPath), cliOptions(cliOptions) {}
17
18 204 void ExternalLinkerInterface::prepare() {
19 // Set target to linker
20
2/4
✓ Branch 3 → 4 taken 204 times.
✗ Branch 3 → 94 not taken.
✓ Branch 4 → 5 taken 204 times.
✗ Branch 4 → 92 not taken.
204 addLinkerFlag("--target=" + cliOptions.targetTriple.str());
21
22 // Static linking
23
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 14 taken 204 times.
204 if (cliOptions.staticLinking)
24 addLinkerFlag("-static");
25
26 // Stripping symbols
27
5/6
✓ Branch 14 → 15 taken 202 times.
✓ Branch 14 → 18 taken 2 times.
✓ Branch 16 → 17 taken 202 times.
✗ Branch 16 → 18 not taken.
✓ Branch 19 → 20 taken 202 times.
✓ Branch 19 → 27 taken 2 times.
204 if (!cliOptions.instrumentation.generateDebugInfo && !cliOptions.targetTriple.isOSDarwin())
28
2/4
✓ Branch 22 → 23 taken 202 times.
✗ Branch 22 → 103 not taken.
✓ Branch 23 → 24 taken 202 times.
✗ Branch 23 → 101 not taken.
404 addLinkerFlag("-Wl,-s");
29
30 // Sanitizers
31
1/6
✓ Branch 27 → 28 taken 204 times.
✗ Branch 27 → 29 not taken.
✗ Branch 27 → 36 not taken.
✗ Branch 27 → 43 not taken.
✗ Branch 27 → 57 not taken.
✗ Branch 27 → 70 not taken.
204 switch (cliOptions.instrumentation.sanitizer) {
32 204 case Sanitizer::NONE:
33 204 break;
34 case Sanitizer::ADDRESS:
35 addLinkerFlag("-lasan");
36 break;
37 case Sanitizer::THREAD:
38 addLinkerFlag("-ltsan");
39 break;
40 case Sanitizer::MEMORY:
41 addLinkerFlag("-L$(clang -print-resource-dir)/lib/x86_64-unknown-linux-gnu");
42 addLinkerFlag("-lclang_rt.msan");
43 requestLibMathLinkage();
44 break;
45 case Sanitizer::TYPE:
46 addLinkerFlag("-L$(clang -print-resource-dir)/lib/x86_64-unknown-linux-gnu");
47 addLinkerFlag("-lclang_rt.tysan");
48 break;
49 }
50
51 // Web Assembly
52
1/2
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 91 taken 204 times.
204 if (cliOptions.targetTriple.isWasm()) {
53 addLinkerFlag("-nostdlib");
54 addLinkerFlag("-Wl,--no-entry");
55 addLinkerFlag("-Wl,--export-all");
56 }
57 204 }
58
59 /**
60 * Start the linking process
61 */
62 204 void ExternalLinkerInterface::link() const {
63
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 204 times.
204 assert(!outputPath.empty());
64
65 // Build the linker command
66
1/2
✓ Branch 5 → 6 taken 204 times.
✗ Branch 5 → 101 not taken.
204 std::stringstream linkerCommandBuilder;
67
1/2
✓ Branch 6 → 7 taken 204 times.
✗ Branch 6 → 99 not taken.
204 const auto [linkerInvokerName, linkerInvokerPath] = SystemUtil::findLinkerInvoker();
68
1/2
✓ Branch 7 → 8 taken 204 times.
✗ Branch 7 → 97 not taken.
204 linkerCommandBuilder << linkerInvokerPath;
69
1/2
✓ Branch 8 → 9 taken 204 times.
✗ Branch 8 → 97 not taken.
204 const auto [linkerName, linkerPath] = SystemUtil::findLinker(cliOptions);
70
2/4
✓ Branch 9 → 10 taken 204 times.
✗ Branch 9 → 95 not taken.
✓ Branch 10 → 11 taken 204 times.
✗ Branch 10 → 95 not taken.
204 linkerCommandBuilder << " -fuse-ld=" << linkerPath;
71 // Append linker flags
72
2/2
✓ Branch 18 → 13 taken 660 times.
✓ Branch 18 → 19 taken 204 times.
864 for (const std::string &linkerFlag : linkerFlags)
73
2/4
✓ Branch 14 → 15 taken 660 times.
✗ Branch 14 → 77 not taken.
✓ Branch 15 → 16 taken 660 times.
✗ Branch 15 → 77 not taken.
660 linkerCommandBuilder << " " << linkerFlag;
74
2/2
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 21 taken 203 times.
204 if (linkLibMath)
75
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 95 not taken.
1 linkerCommandBuilder << " -lm";
76 // Append output path
77
3/6
✓ Branch 21 → 22 taken 204 times.
✗ Branch 21 → 95 not taken.
✓ Branch 22 → 23 taken 204 times.
✗ Branch 22 → 80 not taken.
✓ Branch 23 → 24 taken 204 times.
✗ Branch 23 → 78 not taken.
204 linkerCommandBuilder << " -o " << outputPath.string();
78 // Append object files
79
2/2
✓ Branch 32 → 27 taken 852 times.
✓ Branch 32 → 33 taken 204 times.
1056 for (const std::string &objectFilePath : objectFilePaths)
80
2/4
✓ Branch 28 → 29 taken 852 times.
✗ Branch 28 → 81 not taken.
✓ Branch 29 → 30 taken 852 times.
✗ Branch 29 → 81 not taken.
852 linkerCommandBuilder << " " << objectFilePath;
81
1/2
✓ Branch 33 → 34 taken 204 times.
✗ Branch 33 → 95 not taken.
204 const std::string linkerCommand = linkerCommandBuilder.str();
82
83 // Print status message
84
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 48 taken 204 times.
204 if (cliOptions.printDebugOutput) {
85 std::cout << "\nLinking with: " << linkerInvokerName << " (invoker) / " << linkerName << " (linker)"; // GCOV_EXCL_LINE
86 std::cout << "\nLinker command: " << linkerCommand; // GCOV_EXCL_LINE
87 std::cout << "\nEmitting executable to path: " << outputPath.string() << "\n"; // GCOV_EXCL_LINE
88 }
89
90 // Call the linker
91
1/2
✓ Branch 48 → 49 taken 204 times.
✗ Branch 48 → 93 not taken.
204 Timer timer;
92
1/2
✓ Branch 49 → 50 taken 204 times.
✗ Branch 49 → 93 not taken.
204 timer.start();
93
1/2
✓ Branch 50 → 51 taken 204 times.
✗ Branch 50 → 93 not taken.
204 const auto [output, exitCode] = SystemUtil::exec(linkerCommand);
94
1/2
✓ Branch 51 → 52 taken 204 times.
✗ Branch 51 → 91 not taken.
204 timer.stop();
95
96 // Check for linker error
97 if (exitCode != 0) { // GCOV_EXCL_LINE
98 const std::string errorMessage = "Linker exited with non-zero exit code\nLinker command: " + linkerCommand; // GCOV_EXCL_LINE
99 throw LinkerError(LINKER_ERROR, errorMessage); // GCOV_EXCL_LINE
100 } // GCOV_EXCL_LINE
101
102 // Print linker result if appropriate
103 if (cliOptions.printDebugOutput && !output.empty()) // GCOV_EXCL_LINE
104 std::cout << "Linking result: " << output << "\n\n"; // GCOV_EXCL_LINE
105
106 // Print link time
107 if (cliOptions.printDebugOutput) // GCOV_EXCL_LINE
108 std::cout << "Total link time: " << timer.getDurationMilliseconds() << " ms\n\n"; // GCOV_EXCL_LINE
109 204 }
110
111 /**
112 * Add another object file to be linked when calling 'link()'
113 *
114 * @param objectFilePath Path to the object file
115 */
116 939 void ExternalLinkerInterface::addObjectFilePath(const std::string &objectFilePath) { objectFilePaths.push_back(objectFilePath); }
117
118 /**
119 * Add another linker flag for the call to the linker executable
120 *
121 * @param flag Linker flag
122 */
123 663 void ExternalLinkerInterface::addLinkerFlag(const std::string &flag) { linkerFlags.push_back(flag); }
124
125 /**
126 * Add another source file to compile and link in (C or C++)
127 *
128 * @param additionalSource Additional source file
129 */
130 2 void ExternalLinkerInterface::addAdditionalSourcePath(std::filesystem::path additionalSource) {
131 // Check if the file exists
132 if (!exists(additionalSource)) { // GCOV_EXCL_LINE
133 const std::string msg = "The additional source file '" + additionalSource.string() + "' does not exist"; // GCOV_EXCL_LINE
134 throw CompilerError(IO_ERROR, msg); // GCOV_EXCL_LINE
135 } // GCOV_EXCL_LINE
136
137 // Add the file to the linker
138 2 additionalSource.make_preferred();
139
2/4
✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 31 not taken.
✓ Branch 14 → 15 taken 2 times.
✗ Branch 14 → 29 not taken.
2 addObjectFilePath(additionalSource.string());
140 2 }
141
142 /**
143 * Link against libmath a.k.a. -lm
144 */
145 2 void ExternalLinkerInterface::requestLibMathLinkage() { linkLibMath = true; }
146
147 } // namespace spice::compiler
148