GCC Code Coverage Report


Directory: ../
File: src/linker/ExternalLinkerInterface.cpp
Date: 2025-10-27 22:48:14
Coverage Exec Excl Total
Lines: 85.0% 34 12 52
Functions: 100.0% 5 0 5
Branches: 40.4% 38 56 150

Line Branch Exec Source
1 // Copyright (c) 2021-2025 ChilliBits. All rights reserved.
2
3 #include "ExternalLinkerInterface.h"
4 #include "util/Timer.h"
5
6 #include <iostream>
7
8 #include <exception/CompilerError.h>
9 #include <exception/LinkerError.h>
10 #include <util/FileUtil.h>
11
12 namespace spice::compiler {
13
14 197 void ExternalLinkerInterface::prepare() {
15 // Set target to linker
16
2/4
✓ Branch 3 → 4 taken 197 times.
✗ Branch 3 → 67 not taken.
✓ Branch 4 → 5 taken 197 times.
✗ Branch 4 → 65 not taken.
197 addLinkerFlag("--target=" + cliOptions.targetTriple.str());
17
18 // Static linking
19
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 14 taken 197 times.
197 if (cliOptions.staticLinking)
20 addLinkerFlag("-static");
21
22 // Stripping symbols
23
2/2
✓ Branch 14 → 15 taken 195 times.
✓ Branch 14 → 22 taken 2 times.
197 if (!cliOptions.instrumentation.generateDebugInfo)
24
2/4
✓ Branch 17 → 18 taken 195 times.
✗ Branch 17 → 76 not taken.
✓ Branch 18 → 19 taken 195 times.
✗ Branch 18 → 74 not taken.
390 addLinkerFlag("-Wl,-s");
25
26 // Address sanitizer
27
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 30 taken 197 times.
197 if (cliOptions.instrumentation.sanitizer == Sanitizer::ADDRESS)
28 addLinkerFlag("-lasan");
29
30 // Thread sanitizer
31
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 38 taken 197 times.
197 if (cliOptions.instrumentation.sanitizer == Sanitizer::THREAD)
32 addLinkerFlag("-ltsan");
33
34 // Web Assembly
35
3/6
✓ Branch 39 → 40 taken 197 times.
✗ Branch 39 → 42 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 197 times.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 64 taken 197 times.
197 if (cliOptions.targetArch == TARGET_WASM32 || cliOptions.targetArch == TARGET_WASM64) {
36 addLinkerFlag("-nostdlib");
37 addLinkerFlag("-Wl,--no-entry");
38 addLinkerFlag("-Wl,--export-all");
39 }
40 197 }
41
42 /**
43 * Start the linking process
44 */
45 197 void ExternalLinkerInterface::link() const {
46
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 197 times.
197 assert(!outputPath.empty());
47
48 // Build the linker command
49
1/2
✓ Branch 5 → 6 taken 197 times.
✗ Branch 5 → 104 not taken.
197 std::stringstream linkerCommandBuilder;
50
1/2
✓ Branch 6 → 7 taken 197 times.
✗ Branch 6 → 102 not taken.
197 const auto [linkerInvokerName, linkerInvokerPath] = FileUtil::findLinkerInvoker();
51
1/2
✓ Branch 7 → 8 taken 197 times.
✗ Branch 7 → 100 not taken.
197 linkerCommandBuilder << linkerInvokerPath;
52
1/2
✓ Branch 8 → 9 taken 197 times.
✗ Branch 8 → 100 not taken.
197 const auto [linkerName, linkerPath] = FileUtil::findLinker(cliOptions);
53
2/4
✓ Branch 9 → 10 taken 197 times.
✗ Branch 9 → 98 not taken.
✓ Branch 10 → 11 taken 197 times.
✗ Branch 10 → 98 not taken.
197 linkerCommandBuilder << " -fuse-ld=" << linkerPath;
54 // Append linker flags
55
2/2
✓ Branch 18 → 13 taken 820 times.
✓ Branch 18 → 19 taken 197 times.
1017 for (const std::string &linkerFlag : linkerFlags)
56
2/4
✓ Branch 14 → 15 taken 820 times.
✗ Branch 14 → 77 not taken.
✓ Branch 15 → 16 taken 820 times.
✗ Branch 15 → 77 not taken.
820 linkerCommandBuilder << " " << linkerFlag;
57 // Append output path
58
3/6
✓ Branch 19 → 20 taken 197 times.
✗ Branch 19 → 98 not taken.
✓ Branch 20 → 21 taken 197 times.
✗ Branch 20 → 80 not taken.
✓ Branch 21 → 22 taken 197 times.
✗ Branch 21 → 78 not taken.
197 linkerCommandBuilder << " -o " << outputPath.string();
59 // Append object files
60
2/2
✓ Branch 30 → 25 taken 821 times.
✓ Branch 30 → 31 taken 197 times.
1018 for (const std::string &objectFilePath : objectFilePaths)
61
2/4
✓ Branch 26 → 27 taken 821 times.
✗ Branch 26 → 81 not taken.
✓ Branch 27 → 28 taken 821 times.
✗ Branch 27 → 81 not taken.
821 linkerCommandBuilder << " " << objectFilePath;
62
63 // Print status message
64
1/2
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 43 taken 197 times.
197 if (cliOptions.printDebugOutput) {
65 std::cout << "\nLinking with: " << linkerInvokerName << " (invoker) / " << linkerName << " (linker)"; // GCOV_EXCL_LINE
66 std::cout << "\nEmitting executable to path: " << outputPath.string() << "\n"; // GCOV_EXCL_LINE
67 }
68
69 // Call the linker
70
1/2
✓ Branch 43 → 44 taken 197 times.
✗ Branch 43 → 98 not taken.
197 Timer timer;
71
1/2
✓ Branch 44 → 45 taken 197 times.
✗ Branch 44 → 98 not taken.
197 timer.start();
72
1/2
✓ Branch 45 → 46 taken 197 times.
✗ Branch 45 → 98 not taken.
197 const std::string linkerCommand = linkerCommandBuilder.str();
73
1/2
✓ Branch 46 → 47 taken 197 times.
✗ Branch 46 → 96 not taken.
197 const auto [output, exitCode] = FileUtil::exec(linkerCommand);
74
1/2
✓ Branch 47 → 48 taken 197 times.
✗ Branch 47 → 94 not taken.
197 timer.stop();
75
76 // Check for linker error
77 if (exitCode != 0) // GCOV_EXCL_LINE
78 throw LinkerError(LINKER_ERROR, "Linker exited with non-zero exit code"); // GCOV_EXCL_LINE
79
80 // Print linker result if appropriate
81 if (cliOptions.printDebugOutput && !output.empty()) // GCOV_EXCL_LINE
82 std::cout << "Linking result: " << output << "\n\n"; // GCOV_EXCL_LINE
83
84 // Print link time
85 if (cliOptions.printDebugOutput) // GCOV_EXCL_LINE
86 std::cout << "Total link time: " << timer.getDurationMilliseconds() << " ms\n\n"; // GCOV_EXCL_LINE
87 197 }
88
89 /**
90 * Add another object file to be linked when calling 'link()'
91 *
92 * @param objectFilePath Path to the object file
93 */
94 864 void ExternalLinkerInterface::addObjectFilePath(const std::string &objectFilePath) { objectFilePaths.push_back(objectFilePath); }
95
96 /**
97 * Add another linker flag for the call to the linker executable
98 *
99 * @param flag Linker flag
100 */
101 823 void ExternalLinkerInterface::addLinkerFlag(const std::string &flag) { linkerFlags.push_back(flag); }
102
103 /**
104 * Add another source file to compile and link in (C or C++)
105 *
106 * @param additionalSource Additional source file
107 */
108 2 void ExternalLinkerInterface::addAdditionalSourcePath(std::filesystem::path additionalSource) {
109 // Check if the file exists
110 if (!exists(additionalSource)) { // GCOV_EXCL_LINE
111 const std::string msg = "The additional source file '" + additionalSource.string() + "' does not exist"; // GCOV_EXCL_LINE
112 throw CompilerError(IO_ERROR, msg); // GCOV_EXCL_LINE
113 } // GCOV_EXCL_LINE
114
115 // Add the file to the linker
116 2 additionalSource.make_preferred();
117
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());
118 2 }
119
120 } // namespace spice::compiler
121