GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 50.5% 52 / 22 / 125
Functions: 80.0% 8 / 0 / 10
Branches: 26.6% 50 / 94 / 282

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/GlobalDefinitions.h>
11 #include <util/SystemUtil.h>
12 #include <util/Timer.h>
13
14 namespace spice::compiler {
15
16 452 ExternalLinkerInterface::ExternalLinkerInterface(const CliOptions &cliOptions)
17 452 : outputPath(cliOptions.outputPath), cliOptions(cliOptions) {}
18
19 207 void ExternalLinkerInterface::prepare() {
20 // Set target to linker
21
2/4
✓ Branch 3 → 4 taken 207 times.
✗ Branch 3 → 104 not taken.
✓ Branch 4 → 5 taken 207 times.
✗ Branch 4 → 102 not taken.
207 addLinkerFlag("--target=" + cliOptions.targetTriple.str());
22
23 // Static linking
24
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 14 taken 207 times.
207 if (cliOptions.outputContainer == OutputContainer::SHARED_LIBRARY) {
25 addLinkerFlag("-shared");
26
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 22 taken 207 times.
207 } else if (cliOptions.staticLinking) {
27 addLinkerFlag("-static");
28 }
29
30 // The following flags only make sense if we want to emit an executable
31
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 207 times.
207 if (cliOptions.outputContainer != OutputContainer::EXECUTABLE)
32 return;
33
34 // Stripping symbols
35
5/6
✓ Branch 24 → 25 taken 205 times.
✓ Branch 24 → 28 taken 2 times.
✓ Branch 26 → 27 taken 205 times.
✗ Branch 26 → 28 not taken.
✓ Branch 29 → 30 taken 205 times.
✓ Branch 29 → 37 taken 2 times.
207 if (!cliOptions.instrumentation.generateDebugInfo && !cliOptions.targetTriple.isOSDarwin())
36
2/4
✓ Branch 32 → 33 taken 205 times.
✗ Branch 32 → 119 not taken.
✓ Branch 33 → 34 taken 205 times.
✗ Branch 33 → 117 not taken.
410 addLinkerFlag("-Wl,-s");
37
38 // Sanitizers
39
1/6
✓ Branch 37 → 38 taken 207 times.
✗ Branch 37 → 39 not taken.
✗ Branch 37 → 46 not taken.
✗ Branch 37 → 53 not taken.
✗ Branch 37 → 67 not taken.
✗ Branch 37 → 80 not taken.
207 switch (cliOptions.instrumentation.sanitizer) {
40 207 case Sanitizer::NONE:
41 207 break;
42 case Sanitizer::ADDRESS:
43 addLinkerFlag("-lasan");
44 break;
45 case Sanitizer::THREAD:
46 addLinkerFlag("-ltsan");
47 break;
48 case Sanitizer::MEMORY:
49 addLinkerFlag("-L$(clang -print-resource-dir)/lib/x86_64-unknown-linux-gnu");
50 addLinkerFlag("-lclang_rt.msan");
51 requestLibMathLinkage();
52 break;
53 case Sanitizer::TYPE:
54 addLinkerFlag("-L$(clang -print-resource-dir)/lib/x86_64-unknown-linux-gnu");
55 addLinkerFlag("-lclang_rt.tysan");
56 break;
57 }
58
59 // Web Assembly
60
1/2
✗ Branch 81 → 82 not taken.
✓ Branch 81 → 101 taken 207 times.
207 if (cliOptions.targetTriple.isWasm()) {
61 addLinkerFlag("-nostdlib");
62 addLinkerFlag("-Wl,--no-entry");
63 addLinkerFlag("-Wl,--export-all");
64 }
65 }
66
67 207 void ExternalLinkerInterface::run() const {
68
1/4
✓ Branch 2 → 3 taken 207 times.
✗ Branch 2 → 4 not taken.
✗ Branch 2 → 6 not taken.
✗ Branch 2 → 7 not taken.
207 switch (cliOptions.outputContainer) {
69 207 case OutputContainer::EXECUTABLE:
70 case OutputContainer::SHARED_LIBRARY:
71 207 link();
72 207 break;
73 case OutputContainer::STATIC_LIBRARY:
74 archive();
75 break;
76 case OutputContainer::OBJECT_FILE:
77 // No linking necessary
78 break;
79 default:
80 assert_fail("Unknown output container");
81 }
82 207 }
83
84 /**
85 * Cleanup intermediary object files
86 */
87 void ExternalLinkerInterface::cleanup() const {
88 // Cleanup intermediary object files
89 const char *objFileExt = SystemUtil::getOutputFileExtension(cliOptions, cliOptions.outputContainer);
90 if (cliOptions.outputContainer != OutputContainer::OBJECT_FILE && !cliOptions.dump.dumpToFiles)
91 for (const std::filesystem::path &path : linkedFiles)
92 if (path.extension() == objFileExt)
93 std::filesystem::remove(path);
94 }
95
96 /**
97 * Link the object files to an executable
98 */
99 207 void ExternalLinkerInterface::link() const {
100
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 207 times.
207 assert(!outputPath.empty());
101
102 // Build the linker command
103
1/2
✓ Branch 5 → 6 taken 207 times.
✗ Branch 5 → 106 not taken.
207 std::stringstream commandBuilder;
104
1/2
✓ Branch 6 → 7 taken 207 times.
✗ Branch 6 → 104 not taken.
207 const auto [linkerInvokerName, linkerInvokerPath] = SystemUtil::findLinkerInvoker();
105
1/2
✓ Branch 7 → 8 taken 207 times.
✗ Branch 7 → 102 not taken.
207 commandBuilder << linkerInvokerPath;
106
1/2
✓ Branch 8 → 9 taken 207 times.
✗ Branch 8 → 102 not taken.
207 const auto [linkerName, linkerPath] = SystemUtil::findLinker(cliOptions);
107
2/4
✓ Branch 9 → 10 taken 207 times.
✗ Branch 9 → 100 not taken.
✓ Branch 10 → 11 taken 207 times.
✗ Branch 10 → 100 not taken.
207 commandBuilder << " -fuse-ld=" << linkerPath;
108 // Append linker flags
109
2/2
✓ Branch 18 → 13 taken 666 times.
✓ Branch 18 → 19 taken 207 times.
873 for (const std::string &linkerFlag : linkerFlags)
110
2/4
✓ Branch 14 → 15 taken 666 times.
✗ Branch 14 → 79 not taken.
✓ Branch 15 → 16 taken 666 times.
✗ Branch 15 → 79 not taken.
666 commandBuilder << " " << linkerFlag;
111
2/2
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 21 taken 206 times.
207 if (linkLibMath)
112
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 100 not taken.
1 commandBuilder << " -lm";
113 // Append output path
114
3/6
✓ Branch 21 → 22 taken 207 times.
✗ Branch 21 → 100 not taken.
✓ Branch 22 → 23 taken 207 times.
✗ Branch 22 → 82 not taken.
✓ Branch 23 → 24 taken 207 times.
✗ Branch 23 → 80 not taken.
207 commandBuilder << " -o " << outputPath.string();
115 // Append object files
116
2/2
✓ Branch 34 → 27 taken 855 times.
✓ Branch 34 → 35 taken 207 times.
1062 for (const std::filesystem::path &objectFilePath : linkedFiles)
117
3/6
✓ Branch 28 → 29 taken 855 times.
✗ Branch 28 → 86 not taken.
✓ Branch 29 → 30 taken 855 times.
✗ Branch 29 → 85 not taken.
✓ Branch 30 → 31 taken 855 times.
✗ Branch 30 → 83 not taken.
855 commandBuilder << " " << objectFilePath.string();
118
1/2
✓ Branch 35 → 36 taken 207 times.
✗ Branch 35 → 100 not taken.
207 const std::string command = commandBuilder.str();
119
120 // Print status message
121
1/2
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 50 taken 207 times.
207 if (cliOptions.printDebugOutput) {
122 std::cout << "\nLinking with: " << linkerInvokerName << " (invoker) / " << linkerName << " (linker)"; // GCOV_EXCL_LINE
123 std::cout << "\nLinker command: " << command; // GCOV_EXCL_LINE
124 std::cout << "\nEmitting executable to path: " << outputPath.string() << "\n"; // GCOV_EXCL_LINE
125 }
126
127 // Call the linker
128
1/2
✓ Branch 50 → 51 taken 207 times.
✗ Branch 50 → 98 not taken.
207 Timer timer;
129
1/2
✓ Branch 51 → 52 taken 207 times.
✗ Branch 51 → 98 not taken.
207 timer.start();
130
1/2
✓ Branch 52 → 53 taken 207 times.
✗ Branch 52 → 98 not taken.
207 const auto [output, exitCode] = SystemUtil::exec(command);
131
1/2
✓ Branch 53 → 54 taken 207 times.
✗ Branch 53 → 96 not taken.
207 timer.stop();
132
133 // Check for linker error
134 if (exitCode != 0) { // GCOV_EXCL_LINE
135 const std::string errorMessage = "Linker exited with non-zero exit code\nLinker command: " + command; // GCOV_EXCL_LINE
136 throw LinkerError(LINKER_ERROR, errorMessage); // GCOV_EXCL_LINE
137 } // GCOV_EXCL_LINE
138
139 // Print linker result if appropriate
140 if (cliOptions.printDebugOutput && !output.empty()) // GCOV_EXCL_LINE
141 std::cout << "Linking result: " << output << "\n\n"; // GCOV_EXCL_LINE
142
143 // Print link time
144 if (cliOptions.printDebugOutput) // GCOV_EXCL_LINE
145 std::cout << "Total link time: " << timer.getDurationMilliseconds() << " ms\n\n"; // GCOV_EXCL_LINE
146 207 }
147
148 /**
149 * Archive the object files to a static library
150 */
151 void ExternalLinkerInterface::archive() const {
152 assert(!outputPath.empty());
153
154 // Build the archiver command
155 std::stringstream commandBuilder;
156 const auto [archiverName, archiverPath] = SystemUtil::findArchiver();
157 commandBuilder << archiverPath;
158 commandBuilder << " rcs "; // r = insert files into archive; c = create archive if not existing, s = create archive index
159 commandBuilder << outputPath.string();
160 for (const std::filesystem::path &path : linkedFiles)
161 commandBuilder << " " << path.string();
162 const std::string command = commandBuilder.str();
163
164 // Print status message
165 if (cliOptions.printDebugOutput) {
166 std::cout << "\nArchiving with: " << archiverName; // GCOV_EXCL_LINE
167 std::cout << "\nArchiver command: " << command; // GCOV_EXCL_LINE
168 std::cout << "\nEmitting static library to path: " << outputPath.string() << "\n"; // GCOV_EXCL_LINE
169 }
170
171 // Call the archiver
172 Timer timer;
173 timer.start();
174 const auto [output, exitCode] = SystemUtil::exec(command);
175 timer.stop();
176
177 // Check for linker error
178 if (exitCode != 0) { // GCOV_EXCL_LINE
179 const std::string errorMessage = "Archiver exited with non-zero exit code\nArchiver command: " + command; // GCOV_EXCL_LINE
180 throw LinkerError(LINKER_ERROR, errorMessage); // GCOV_EXCL_LINE
181 } // GCOV_EXCL_LINE
182
183 // Print linker result if appropriate
184 if (cliOptions.printDebugOutput && !output.empty()) // GCOV_EXCL_LINE
185 std::cout << "Archiving result: " << output << "\n\n"; // GCOV_EXCL_LINE
186
187 // Print link time
188 if (cliOptions.printDebugOutput) // GCOV_EXCL_LINE
189 std::cout << "Total archive time: " << timer.getDurationMilliseconds() << " ms\n\n"; // GCOV_EXCL_LINE
190 }
191
192 /**
193 * Add another object file to be linked when calling 'link()'
194 *
195 * @param path Path to the object file
196 */
197 942 void ExternalLinkerInterface::addFileToLinkage(const std::filesystem::path &path) { linkedFiles.push_back(path); }
198
199 /**
200 * Add another linker flag for the call to the linker executable
201 *
202 * @param flag Linker flag
203 */
204 669 void ExternalLinkerInterface::addLinkerFlag(const std::string &flag) { linkerFlags.push_back(flag); }
205
206 /**
207 * Add another source file to compile and link in (C or C++)
208 *
209 * @param additionalSource Additional source file
210 */
211 3 void ExternalLinkerInterface::addAdditionalSourcePath(std::filesystem::path additionalSource) {
212 // Check if the file exists
213
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 12 taken 2 times.
3 if (!exists(additionalSource)) {
214
3/6
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 22 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 20 not taken.
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 18 not taken.
1 const std::string msg = "The additional source file '" + additionalSource.string() + "' does not exist";
215
1/2
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 24 not taken.
1 throw CompilerError(IO_ERROR, msg);
216 1 }
217
218 // Add the file to the linker
219
1/2
✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 30 not taken.
2 additionalSource = canonical(additionalSource);
220 2 additionalSource.make_preferred();
221 2 addFileToLinkage(additionalSource);
222 2 }
223
224 /**
225 * Link against libmath a.k.a. -lm
226 */
227 2 void ExternalLinkerInterface::requestLibMathLinkage() { linkLibMath = true; }
228
229 } // namespace spice::compiler
230