| 1 | //===- LinkCLI.cpp --------------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Implements the default (no subcommand) linking action. Inputs are read one |
| 10 | // at a time and folded into the link unit as they are read, so the first |
| 11 | // input that cannot be accepted is the one reported. |
| 12 | // |
| 13 | // The target triple is fixed from the first input (or from --target-triple) |
| 14 | // before the linker is constructed. Validating every later input against it |
| 15 | // happens here rather than in EntityLinker: choosing which inputs belong to |
| 16 | // a target is a command line concern, and EntityLinker treats a mismatch as |
| 17 | // a fatal precondition violation. |
| 18 | // |
| 19 | //===----------------------------------------------------------------------===// |
| 20 | |
| 21 | #include "LinkCLI.h" |
| 22 | |
| 23 | #include "clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchSharedLibrary.h" |
| 24 | #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" |
| 25 | #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" |
| 26 | #include "clang/ScalableStaticAnalysis/Core/Support/ErrorBuilder.h" |
| 27 | #include "clang/ScalableStaticAnalysis/Core/Support/FormatProviders.h" |
| 28 | #include "llvm/ADT/Sequence.h" |
| 29 | #include "llvm/Support/Path.h" |
| 30 | #include <cassert> |
| 31 | #include <memory> |
| 32 | #include <utility> |
| 33 | #include <variant> |
| 34 | |
| 35 | using namespace llvm; |
| 36 | using namespace clang::ssaf; |
| 37 | |
| 38 | namespace path = llvm::sys::path; |
| 39 | |
| 40 | namespace { |
| 41 | |
| 42 | //===----------------------------------------------------------------------===// |
| 43 | // Error Messages |
| 44 | //===----------------------------------------------------------------------===// |
| 45 | |
| 46 | constexpr const char *ReadingArtifact = "Reading artifact '{0}'" ; |
| 47 | |
| 48 | constexpr const char *LinkingArtifact = "Linking artifact '{0}'" ; |
| 49 | |
| 50 | constexpr const char *NoInputs = |
| 51 | "no input artifacts: at least one input is required" ; |
| 52 | |
| 53 | constexpr const char *NoMembersToInferFrom = |
| 54 | "cannot infer target triple from '{0}': multi-arch static library has no " |
| 55 | "members; pass --target-triple" ; |
| 56 | |
| 57 | constexpr const char *AmbiguousMembersToInferFrom = |
| 58 | "cannot infer target triple from '{0}': multi-arch static library has {1} " |
| 59 | "members; pass --target-triple to select one" ; |
| 60 | |
| 61 | constexpr const char *UnsupportedSharedInput = |
| 62 | "'{0}' is a {1}: linking against shared libraries is not yet supported" ; |
| 63 | |
| 64 | constexpr const char *LinkUnitSummaryName = "link unit summary" ; |
| 65 | constexpr const char *MultiArchSharedLibraryName = "multi-arch shared library" ; |
| 66 | |
| 67 | //===----------------------------------------------------------------------===// |
| 68 | // ArtifactEncoding Helpers |
| 69 | //===----------------------------------------------------------------------===// |
| 70 | |
| 71 | /// Returns the human readable kind of an artifact the linker cannot consume. |
| 72 | /// |
| 73 | /// Only the shared-library family reaches this: every linkable alternative is |
| 74 | /// handled before it is called. The static_assert makes a new alternative a |
| 75 | /// compile error here rather than an unhandled case at runtime. |
| 76 | llvm::StringRef unsupportedInputKindName(const ArtifactEncoding &E) { |
| 77 | static_assert(std::variant_size_v<ArtifactEncoding> == 5, |
| 78 | "unsupportedInputKindName must cover every ArtifactEncoding " |
| 79 | "alternative the linker cannot consume" ); |
| 80 | |
| 81 | if (std::holds_alternative<LUSummaryEncoding>(v: E)) { |
| 82 | return LinkUnitSummaryName; |
| 83 | } |
| 84 | |
| 85 | assert( |
| 86 | std::holds_alternative<MultiArchSharedLibrary>(E) && |
| 87 | "linkable ArtifactEncoding alternatives must be handled by the caller" ); |
| 88 | return MultiArchSharedLibraryName; |
| 89 | } |
| 90 | |
| 91 | } // namespace |
| 92 | |
| 93 | namespace clang::ssaf { |
| 94 | |
| 95 | void LinkCLI::run(llvm::TimerGroup &TG, llvm::ArrayRef<std::string> InputPaths, |
| 96 | llvm::StringRef OutputPath, llvm::StringRef TargetTriple, |
| 97 | bool Verbose, bool Time) { |
| 98 | this->InputPaths = InputPaths; |
| 99 | this->OutputPath = OutputPath; |
| 100 | this->TargetTriple = TargetTriple; |
| 101 | this->Verbose = Verbose; |
| 102 | this->Time = Time; |
| 103 | |
| 104 | llvm::Timer TValidate("validate" , "Validate Input" , TG); |
| 105 | llvm::Timer TRead("read" , "Read Artifacts" , TG); |
| 106 | llvm::Timer TLink("link" , "Link Artifacts" , TG); |
| 107 | llvm::Timer TWrite("write" , "Write Link Unit Summary" , TG); |
| 108 | |
| 109 | // Nesting depth for indenting verbose notes. |
| 110 | const unsigned Level = 0; |
| 111 | |
| 112 | info(Verbose, Level, Fmt: "Linking started." ); |
| 113 | |
| 114 | validate(Level: Level + 1, TValidate); |
| 115 | |
| 116 | LUSummaryEncoding Output = link(Level: Level + 1, TRead, TLink); |
| 117 | |
| 118 | write(Output, Level: Level + 1, TWrite); |
| 119 | |
| 120 | info(Verbose, Level, Fmt: "Linking finished." ); |
| 121 | |
| 122 | // A second run() should start from a clean slate. |
| 123 | InputFiles.clear(); |
| 124 | ExplicitTriple.reset(); |
| 125 | } |
| 126 | |
| 127 | void LinkCLI::validate(unsigned Level, llvm::Timer &TValidate) { |
| 128 | info(Verbose, Level, Fmt: "Validating input." ); |
| 129 | |
| 130 | llvm::TimeRegion _(Time ? &TValidate : nullptr); |
| 131 | |
| 132 | OutputFile = FormatFile::fromOutputPath(Path: OutputPath); |
| 133 | LinkUnitName = path::stem(path: OutputFile.Path).str(); |
| 134 | info(Verbose, Level: Level + 1, Fmt: "Validated output path '{0}'." , Args&: OutputFile.Path); |
| 135 | |
| 136 | if (InputPaths.empty()) { |
| 137 | fail(Msg: NoInputs); |
| 138 | } |
| 139 | for (const auto &InputPath : InputPaths) { |
| 140 | InputFiles.push_back(x: FormatFile::fromInputPath(Path: InputPath)); |
| 141 | } |
| 142 | info(Verbose, Level: Level + 1, Fmt: "Validated {0} input artifact path(s)." , |
| 143 | Args: InputFiles.size()); |
| 144 | |
| 145 | if (!TargetTriple.empty()) { |
| 146 | ExplicitTriple = parseTargetTripleOrFail(FlagName: "--target-triple" , Value: TargetTriple); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | LUSummaryEncoding LinkCLI::link(unsigned Level, llvm::Timer &TRead, |
| 151 | llvm::Timer &TLink) { |
| 152 | info(Verbose, Level, Fmt: "Creating link unit." ); |
| 153 | |
| 154 | const unsigned InputLevel = Level + 1; |
| 155 | info(Verbose, Level: InputLevel, Fmt: "Linking artifacts." ); |
| 156 | |
| 157 | // The target triple comes from the first input, so it has to be read before |
| 158 | // the linker can be constructed. |
| 159 | constexpr size_t FirstIndex = 0; |
| 160 | ArtifactEncoding First = |
| 161 | readInput(Input: InputFiles[FirstIndex], Index: FirstIndex, Level: InputLevel + 1, TRead); |
| 162 | |
| 163 | llvm::Triple LinkUnitTriple = |
| 164 | resolveTargetTriple(First, SourceFile: InputFiles[FirstIndex].Path, Level: InputLevel + 1); |
| 165 | |
| 166 | NestedBuildNamespace LUNamespace( |
| 167 | BuildNamespace(BuildNamespaceKind::LinkUnit, LinkUnitName)); |
| 168 | EntityLinker EL(LinkUnitTriple, LUNamespace); |
| 169 | |
| 170 | linkInput(EL, Encoding: std::move(First), SourceFile: InputFiles[FirstIndex].Path, Index: FirstIndex, |
| 171 | Level: InputLevel + 1, TLink); |
| 172 | for (size_t Index : llvm::seq<size_t>(Begin: FirstIndex + 1, End: InputFiles.size())) { |
| 173 | linkInput(EL, Encoding: readInput(Input: InputFiles[Index], Index, Level: InputLevel + 1, TRead), |
| 174 | SourceFile: InputFiles[Index].Path, Index, Level: InputLevel + 1, TLink); |
| 175 | } |
| 176 | |
| 177 | info(Verbose, Level: InputLevel, Fmt: "Linked {0} translation unit(s)." , |
| 178 | Args: EL.getLinkedTUCount()); |
| 179 | info(Verbose, Level: InputLevel, Fmt: "Target namespace: '{0}'." , Args&: LUNamespace); |
| 180 | |
| 181 | return std::move(EL).takeOutput(); |
| 182 | } |
| 183 | |
| 184 | ArtifactEncoding LinkCLI::readInput(const FormatFile &Input, size_t Index, |
| 185 | unsigned Level, llvm::Timer &TRead) { |
| 186 | info(Verbose, Level, Fmt: "[{0}/{1}] Reading '{2}'." , Args: Index + 1, Args: InputFiles.size(), |
| 187 | Args: Input.Path); |
| 188 | |
| 189 | llvm::TimeRegion _(Time ? &TRead : nullptr); |
| 190 | |
| 191 | auto ExpectedEncoding = Input.Format->readArtifactEncoding(Path: Input.Path); |
| 192 | if (!ExpectedEncoding) { |
| 193 | fail(Err: ErrorBuilder::wrap(E: ExpectedEncoding.takeError()) |
| 194 | .context(Fmt: ReadingArtifact, ArgVals: Input.Path) |
| 195 | .build()); |
| 196 | } |
| 197 | return std::move(*ExpectedEncoding); |
| 198 | } |
| 199 | |
| 200 | llvm::Triple LinkCLI::resolveTargetTriple(const ArtifactEncoding &First, |
| 201 | llvm::StringRef SourceFile, |
| 202 | unsigned Level) { |
| 203 | if (ExplicitTriple) { |
| 204 | info(Verbose, Level, Fmt: "Target triple: '{0}' (from --target-triple)." , |
| 205 | Args&: *ExplicitTriple); |
| 206 | return *ExplicitTriple; |
| 207 | } |
| 208 | |
| 209 | auto Inferred = [&]() -> llvm::Triple { |
| 210 | if (const auto *TU = std::get_if<TUSummaryEncoding>(ptr: &First)) { |
| 211 | return TU->getTargetTriple(); |
| 212 | } |
| 213 | |
| 214 | if (const auto *SL = std::get_if<StaticLibrary>(ptr: &First)) { |
| 215 | return SL->TargetTriple; |
| 216 | } |
| 217 | |
| 218 | if (const auto *MASL = std::get_if<MultiArchStaticLibrary>(ptr: &First)) { |
| 219 | // A single member names the target unambiguously; anything else needs the |
| 220 | // architecture to be chosen on the command line. |
| 221 | if (MASL->Members.empty()) { |
| 222 | fail(Fmt: NoMembersToInferFrom, Args&: SourceFile); |
| 223 | } |
| 224 | if (MASL->Members.size() > 1) { |
| 225 | fail(Fmt: AmbiguousMembersToInferFrom, Args&: SourceFile, Args: MASL->Members.size()); |
| 226 | } |
| 227 | return (*MASL->Members.begin())->TargetTriple; |
| 228 | } |
| 229 | |
| 230 | fail(Fmt: UnsupportedSharedInput, Args&: SourceFile, Args: unsupportedInputKindName(E: First)); |
| 231 | }(); |
| 232 | |
| 233 | info(Verbose, Level, Fmt: "Target triple: '{0}' (inferred from '{1}')." , Args&: Inferred, |
| 234 | Args&: SourceFile); |
| 235 | |
| 236 | return Inferred; |
| 237 | } |
| 238 | |
| 239 | void LinkCLI::linkInput(EntityLinker &EL, ArtifactEncoding Encoding, |
| 240 | llvm::StringRef SourceFile, size_t Index, |
| 241 | unsigned Level, llvm::Timer &TLink) { |
| 242 | auto failOnError = [&](llvm::Error Err) { |
| 243 | if (Err) { |
| 244 | fail(Err: ErrorBuilder::wrap(E: std::move(Err)) |
| 245 | .context(Fmt: LinkingArtifact, ArgVals&: SourceFile) |
| 246 | .build()); |
| 247 | } |
| 248 | }; |
| 249 | |
| 250 | if (auto *TU = std::get_if<TUSummaryEncoding>(ptr: &Encoding)) { |
| 251 | info(Verbose, Level, Fmt: "[{0}/{1}] Linking '{2}'." , Args: Index + 1, |
| 252 | Args: InputFiles.size(), Args&: SourceFile); |
| 253 | llvm::TimeRegion _(Time ? &TLink : nullptr); |
| 254 | |
| 255 | failOnError(EL.link(Summary: std::make_unique<TUSummaryEncoding>(args: std::move(*TU)))); |
| 256 | return; |
| 257 | } |
| 258 | |
| 259 | if (auto *SL = std::get_if<StaticLibrary>(ptr: &Encoding)) { |
| 260 | info(Verbose, Level, |
| 261 | Fmt: "[{0}/{1}] Linking '{2}' (static library, {3} member(s))." , Args: Index + 1, |
| 262 | Args: InputFiles.size(), Args&: SourceFile, Args: SL->Members.size()); |
| 263 | llvm::TimeRegion _(Time ? &TLink : nullptr); |
| 264 | |
| 265 | failOnError(EL.link(Library: std::make_unique<StaticLibrary>(args: std::move(*SL)))); |
| 266 | return; |
| 267 | } |
| 268 | |
| 269 | if (auto *MASL = std::get_if<MultiArchStaticLibrary>(ptr: &Encoding)) { |
| 270 | info(Verbose, Level, |
| 271 | Fmt: "[{0}/{1}] Linking '{2}' (multi-arch static library, {3} member(s))." , |
| 272 | Args: Index + 1, Args: InputFiles.size(), Args&: SourceFile, Args: MASL->Members.size()); |
| 273 | llvm::TimeRegion _(Time ? &TLink : nullptr); |
| 274 | |
| 275 | failOnError( |
| 276 | EL.link(Library: std::make_unique<MultiArchStaticLibrary>(args: std::move(*MASL)))); |
| 277 | return; |
| 278 | } |
| 279 | |
| 280 | fail(Fmt: UnsupportedSharedInput, Args&: SourceFile, Args: unsupportedInputKindName(E: Encoding)); |
| 281 | } |
| 282 | |
| 283 | void LinkCLI::write(const LUSummaryEncoding &Output, unsigned Level, |
| 284 | llvm::Timer &TWrite) { |
| 285 | info(Verbose, Level, Fmt: "Writing link unit summary to '{0}'." , Args&: OutputFile.Path); |
| 286 | |
| 287 | llvm::TimeRegion _(Time ? &TWrite : nullptr); |
| 288 | |
| 289 | if (auto Err = |
| 290 | OutputFile.Format->writeLUSummaryEncoding(SummaryEncoding: Output, Path: OutputFile.Path)) { |
| 291 | fail(Err: std::move(Err)); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | } // namespace clang::ssaf |
| 296 | |