| 1 | //===- DriverUtils.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 | // This file contains utility functions for the driver. Because there |
| 10 | // are so many small functions, we created this separate file to make |
| 11 | // Driver.cpp less cluttered. |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
| 15 | #include "COFFLinkerContext.h" |
| 16 | #include "Driver.h" |
| 17 | #include "Symbols.h" |
| 18 | #include "lld/Common/ErrorHandler.h" |
| 19 | #include "lld/Common/Memory.h" |
| 20 | #include "llvm/ADT/StringExtras.h" |
| 21 | #include "llvm/ADT/StringSwitch.h" |
| 22 | #include "llvm/BinaryFormat/COFF.h" |
| 23 | #include "llvm/IR/Mangler.h" |
| 24 | #include "llvm/Object/COFF.h" |
| 25 | #include "llvm/Object/WindowsResource.h" |
| 26 | #include "llvm/Option/Arg.h" |
| 27 | #include "llvm/Option/ArgList.h" |
| 28 | #include "llvm/Option/Option.h" |
| 29 | #include "llvm/Support/CommandLine.h" |
| 30 | #include "llvm/Support/MathExtras.h" |
| 31 | #include "llvm/Support/Process.h" |
| 32 | #include "llvm/Support/Program.h" |
| 33 | #include "llvm/Support/raw_ostream.h" |
| 34 | #include "llvm/WindowsManifest/WindowsManifestMerger.h" |
| 35 | #include <memory> |
| 36 | #include <optional> |
| 37 | |
| 38 | using namespace llvm::COFF; |
| 39 | using namespace llvm::object; |
| 40 | using namespace llvm::opt; |
| 41 | using namespace llvm; |
| 42 | using llvm::sys::Process; |
| 43 | |
| 44 | namespace lld { |
| 45 | namespace coff { |
| 46 | namespace { |
| 47 | |
| 48 | const uint16_t SUBLANG_ENGLISH_US = 0x0409; |
| 49 | const uint16_t RT_MANIFEST = 24; |
| 50 | |
| 51 | class Executor { |
| 52 | public: |
| 53 | explicit Executor(StringRef s) : prog(saver().save(S: s)) {} |
| 54 | void add(StringRef s) { args.push_back(x: saver().save(S: s)); } |
| 55 | void add(std::string &s) { args.push_back(x: saver().save(S: s)); } |
| 56 | void add(Twine s) { args.push_back(x: saver().save(S: s)); } |
| 57 | void add(const char *s) { args.push_back(x: saver().save(S: s)); } |
| 58 | |
| 59 | void run() { |
| 60 | ErrorOr<std::string> exeOrErr = sys::findProgramByName(Name: prog); |
| 61 | if (auto ec = exeOrErr.getError()) |
| 62 | fatal(msg: "unable to find " + prog + " in PATH: " + ec.message()); |
| 63 | StringRef exe = saver().save(S: *exeOrErr); |
| 64 | args.insert(position: args.begin(), x: exe); |
| 65 | |
| 66 | if (sys::ExecuteAndWait(Program: args[0], Args: args) != 0) |
| 67 | fatal(msg: "ExecuteAndWait failed: " + |
| 68 | llvm::join(Begin: args.begin(), End: args.end(), Separator: " " )); |
| 69 | } |
| 70 | |
| 71 | private: |
| 72 | StringRef prog; |
| 73 | std::vector<StringRef> args; |
| 74 | }; |
| 75 | |
| 76 | } // anonymous namespace |
| 77 | |
| 78 | // Parses a string in the form of "<integer>[,<integer>]". |
| 79 | void LinkerDriver::parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { |
| 80 | auto [s1, s2] = arg.split(Separator: ','); |
| 81 | if (s1.getAsInteger(Radix: 0, Result&: *addr)) |
| 82 | Fatal(ctx) << "invalid number: " << s1; |
| 83 | if (size && !s2.empty() && s2.getAsInteger(Radix: 0, Result&: *size)) |
| 84 | Fatal(ctx) << "invalid number: " << s2; |
| 85 | } |
| 86 | |
| 87 | // Parses a string in the form of "<integer>[.<integer>]". |
| 88 | // If second number is not present, Minor is set to 0. |
| 89 | void LinkerDriver::parseVersion(StringRef arg, uint32_t *major, |
| 90 | uint32_t *minor) { |
| 91 | auto [s1, s2] = arg.split(Separator: '.'); |
| 92 | if (s1.getAsInteger(Radix: 10, Result&: *major)) |
| 93 | Fatal(ctx) << "invalid number: " << s1; |
| 94 | *minor = 0; |
| 95 | if (!s2.empty() && s2.getAsInteger(Radix: 10, Result&: *minor)) |
| 96 | Fatal(ctx) << "invalid number: " << s2; |
| 97 | } |
| 98 | |
| 99 | void LinkerDriver::parseGuard(StringRef fullArg) { |
| 100 | SmallVector<StringRef, 1> splitArgs; |
| 101 | fullArg.split(A&: splitArgs, Separator: "," ); |
| 102 | for (StringRef arg : splitArgs) { |
| 103 | if (arg.equals_insensitive(RHS: "no" )) |
| 104 | ctx.config.guardCF = GuardCFLevel::Off; |
| 105 | else if (arg.equals_insensitive(RHS: "nolongjmp" )) |
| 106 | ctx.config.guardCF &= ~GuardCFLevel::LongJmp; |
| 107 | else if (arg.equals_insensitive(RHS: "noehcont" )) |
| 108 | ctx.config.guardCF &= ~GuardCFLevel::EHCont; |
| 109 | else if (arg.equals_insensitive(RHS: "cf" ) || arg.equals_insensitive(RHS: "longjmp" )) |
| 110 | ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp; |
| 111 | else if (arg.equals_insensitive(RHS: "ehcont" )) |
| 112 | ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont; |
| 113 | else |
| 114 | Fatal(ctx) << "invalid argument to /guard: " << arg; |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]". |
| 119 | void LinkerDriver::parseSubsystem(StringRef arg, WindowsSubsystem *sys, |
| 120 | uint32_t *major, uint32_t *minor, |
| 121 | bool *gotVersion) { |
| 122 | auto [sysStr, ver] = arg.split(Separator: ','); |
| 123 | std::string sysStrLower = sysStr.lower(); |
| 124 | *sys = StringSwitch<WindowsSubsystem>(sysStrLower) |
| 125 | .Case(S: "boot_application" , Value: IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) |
| 126 | .Case(S: "console" , Value: IMAGE_SUBSYSTEM_WINDOWS_CUI) |
| 127 | .Case(S: "default" , Value: IMAGE_SUBSYSTEM_UNKNOWN) |
| 128 | .Case(S: "efi_application" , Value: IMAGE_SUBSYSTEM_EFI_APPLICATION) |
| 129 | .Case(S: "efi_boot_service_driver" , Value: IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) |
| 130 | .Case(S: "efi_rom" , Value: IMAGE_SUBSYSTEM_EFI_ROM) |
| 131 | .Case(S: "efi_runtime_driver" , Value: IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) |
| 132 | .Case(S: "native" , Value: IMAGE_SUBSYSTEM_NATIVE) |
| 133 | .Case(S: "posix" , Value: IMAGE_SUBSYSTEM_POSIX_CUI) |
| 134 | .Case(S: "windows" , Value: IMAGE_SUBSYSTEM_WINDOWS_GUI) |
| 135 | .Default(Value: IMAGE_SUBSYSTEM_UNKNOWN); |
| 136 | if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default" ) |
| 137 | Fatal(ctx) << "unknown subsystem: " << sysStr; |
| 138 | if (!ver.empty()) |
| 139 | parseVersion(arg: ver, major, minor); |
| 140 | if (gotVersion) |
| 141 | *gotVersion = !ver.empty(); |
| 142 | } |
| 143 | |
| 144 | // Parse a string of the form of "<from>=<to>". |
| 145 | // Results are directly written to Config. |
| 146 | void LinkerDriver::parseMerge(StringRef s) { |
| 147 | auto [from, to] = s.split(Separator: '='); |
| 148 | if (from.empty() || to.empty()) |
| 149 | Fatal(ctx) << "/merge: invalid argument: " << s; |
| 150 | if (from == ".rsrc" || to == ".rsrc" ) |
| 151 | Fatal(ctx) << "/merge: cannot merge '.rsrc' with any section" ; |
| 152 | if (from == ".reloc" || to == ".reloc" ) |
| 153 | Fatal(ctx) << "/merge: cannot merge '.reloc' with any section" ; |
| 154 | auto pair = ctx.config.merge.insert(x: std::make_pair(x&: from, y&: to)); |
| 155 | bool inserted = pair.second; |
| 156 | if (!inserted) { |
| 157 | StringRef existing = pair.first->second; |
| 158 | if (existing != to) |
| 159 | Warn(ctx) << s << ": already merged into " << existing; |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | void LinkerDriver::parsePDBPageSize(StringRef s) { |
| 164 | int v; |
| 165 | if (s.getAsInteger(Radix: 0, Result&: v)) { |
| 166 | Err(ctx) << "/pdbpagesize: invalid argument: " << s; |
| 167 | return; |
| 168 | } |
| 169 | if (v != 4096 && v != 8192 && v != 16384 && v != 32768) { |
| 170 | Err(ctx) << "/pdbpagesize: invalid argument: " << s; |
| 171 | return; |
| 172 | } |
| 173 | |
| 174 | ctx.config.pdbPageSize = v; |
| 175 | } |
| 176 | |
| 177 | static uint32_t parseSectionAttributes(COFFLinkerContext &ctx, StringRef s) { |
| 178 | uint32_t ret = 0; |
| 179 | for (char c : s.lower()) { |
| 180 | switch (c) { |
| 181 | case 'd': |
| 182 | ret |= IMAGE_SCN_MEM_DISCARDABLE; |
| 183 | break; |
| 184 | case 'e': |
| 185 | ret |= IMAGE_SCN_MEM_EXECUTE; |
| 186 | break; |
| 187 | case 'k': |
| 188 | ret |= IMAGE_SCN_MEM_NOT_CACHED; |
| 189 | break; |
| 190 | case 'p': |
| 191 | ret |= IMAGE_SCN_MEM_NOT_PAGED; |
| 192 | break; |
| 193 | case 'r': |
| 194 | ret |= IMAGE_SCN_MEM_READ; |
| 195 | break; |
| 196 | case 's': |
| 197 | ret |= IMAGE_SCN_MEM_SHARED; |
| 198 | break; |
| 199 | case 'w': |
| 200 | ret |= IMAGE_SCN_MEM_WRITE; |
| 201 | break; |
| 202 | default: |
| 203 | Fatal(ctx) << "/section: invalid argument: " << s; |
| 204 | } |
| 205 | } |
| 206 | return ret; |
| 207 | } |
| 208 | |
| 209 | // Parses /section option argument. |
| 210 | void LinkerDriver::parseSection(StringRef s) { |
| 211 | auto [name, attrs] = s.split(Separator: ','); |
| 212 | if (name.empty() || attrs.empty()) |
| 213 | Fatal(ctx) << "/section: invalid argument: " << s; |
| 214 | ctx.config.section[name] = parseSectionAttributes(ctx, s: attrs); |
| 215 | } |
| 216 | |
| 217 | void LinkerDriver::parseDosStub(StringRef path) { |
| 218 | std::unique_ptr<MemoryBuffer> stub = |
| 219 | CHECK(MemoryBuffer::getFile(path), "could not open " + path); |
| 220 | size_t bufferSize = stub->getBufferSize(); |
| 221 | const char *bufferStart = stub->getBufferStart(); |
| 222 | // MS link.exe compatibility: |
| 223 | // 1. stub must be greater than or equal to 64 bytes |
| 224 | // 2. stub must start with a valid dos signature 'MZ' |
| 225 | if (bufferSize < 64) |
| 226 | Err(ctx) << "/stub: stub must be greater than or equal to 64 bytes: " |
| 227 | << path; |
| 228 | if (bufferStart[0] != 'M' || bufferStart[1] != 'Z') |
| 229 | Err(ctx) << "/stub: invalid DOS signature: " << path; |
| 230 | ctx.config.dosStub = std::move(stub); |
| 231 | } |
| 232 | |
| 233 | // Parses /functionpadmin option argument. |
| 234 | void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) { |
| 235 | StringRef arg = a->getNumValues() ? a->getValue() : "" ; |
| 236 | if (!arg.empty()) { |
| 237 | // Optional padding in bytes is given. |
| 238 | if (arg.getAsInteger(Radix: 0, Result&: ctx.config.functionPadMin)) |
| 239 | Err(ctx) << "/functionpadmin: invalid argument: " << arg; |
| 240 | return; |
| 241 | } |
| 242 | // No optional argument given. |
| 243 | // Set default padding based on machine, similar to link.exe. |
| 244 | // There is no default padding for ARM platforms. |
| 245 | if (ctx.config.machine == I386) { |
| 246 | ctx.config.functionPadMin = 5; |
| 247 | } else if (ctx.config.machine == AMD64) { |
| 248 | ctx.config.functionPadMin = 6; |
| 249 | } else { |
| 250 | Err(ctx) << "/functionpadmin: invalid argument for this machine: " << arg; |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | // Parses /dependentloadflag option argument. |
| 255 | void LinkerDriver::parseDependentLoadFlags(llvm::opt::Arg *a) { |
| 256 | StringRef arg = a->getNumValues() ? a->getValue() : "" ; |
| 257 | if (!arg.empty()) { |
| 258 | if (arg.getAsInteger(Radix: 0, Result&: ctx.config.dependentLoadFlags)) |
| 259 | Err(ctx) << "/dependentloadflag: invalid argument: " << arg; |
| 260 | return; |
| 261 | } |
| 262 | // MSVC linker reports error "no argument specified", although MSDN describes |
| 263 | // argument as optional. |
| 264 | Err(ctx) << "/dependentloadflag: no argument specified" ; |
| 265 | } |
| 266 | |
| 267 | // Parses a string in the form of "EMBED[,=<integer>]|NO". |
| 268 | // Results are directly written to |
| 269 | // Config. |
| 270 | void LinkerDriver::parseManifest(StringRef arg) { |
| 271 | if (arg.equals_insensitive(RHS: "no" )) { |
| 272 | ctx.config.manifest = Configuration::No; |
| 273 | return; |
| 274 | } |
| 275 | if (!arg.starts_with_insensitive(Prefix: "embed" )) |
| 276 | Fatal(ctx) << "invalid option " << arg; |
| 277 | ctx.config.manifest = Configuration::Embed; |
| 278 | arg = arg.substr(Start: strlen(s: "embed" )); |
| 279 | if (arg.empty()) |
| 280 | return; |
| 281 | if (!arg.starts_with_insensitive(Prefix: ",id=" )) |
| 282 | Fatal(ctx) << "invalid option " << arg; |
| 283 | arg = arg.substr(Start: strlen(s: ",id=" )); |
| 284 | if (arg.getAsInteger(Radix: 0, Result&: ctx.config.manifestID)) |
| 285 | Fatal(ctx) << "invalid option " << arg; |
| 286 | } |
| 287 | |
| 288 | // Parses a string in the form of "level=<string>|uiAccess=<string>|NO". |
| 289 | // Results are directly written to Config. |
| 290 | void LinkerDriver::parseManifestUAC(StringRef arg) { |
| 291 | if (arg.equals_insensitive(RHS: "no" )) { |
| 292 | ctx.config.manifestUAC = false; |
| 293 | return; |
| 294 | } |
| 295 | for (;;) { |
| 296 | arg = arg.ltrim(); |
| 297 | if (arg.empty()) |
| 298 | return; |
| 299 | if (arg.consume_front_insensitive(Prefix: "level=" )) { |
| 300 | std::tie(args&: ctx.config.manifestLevel, args&: arg) = arg.split(Separator: " " ); |
| 301 | continue; |
| 302 | } |
| 303 | if (arg.consume_front_insensitive(Prefix: "uiaccess=" )) { |
| 304 | std::tie(args&: ctx.config.manifestUIAccess, args&: arg) = arg.split(Separator: " " ); |
| 305 | continue; |
| 306 | } |
| 307 | Fatal(ctx) << "invalid option " << arg; |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | // Parses a string in the form of "cd|net[,(cd|net)]*" |
| 312 | // Results are directly written to Config. |
| 313 | void LinkerDriver::parseSwaprun(StringRef arg) { |
| 314 | do { |
| 315 | auto [swaprun, newArg] = arg.split(Separator: ','); |
| 316 | if (swaprun.equals_insensitive(RHS: "cd" )) |
| 317 | ctx.config.swaprunCD = true; |
| 318 | else if (swaprun.equals_insensitive(RHS: "net" )) |
| 319 | ctx.config.swaprunNet = true; |
| 320 | else if (swaprun.empty()) |
| 321 | Err(ctx) << "/swaprun: missing argument" ; |
| 322 | else |
| 323 | Err(ctx) << "/swaprun: invalid argument: " << swaprun; |
| 324 | // To catch trailing commas, e.g. `/spawrun:cd,` |
| 325 | if (newArg.empty() && arg.ends_with(Suffix: "," )) |
| 326 | Err(ctx) << "/swaprun: missing argument" ; |
| 327 | arg = newArg; |
| 328 | } while (!arg.empty()); |
| 329 | } |
| 330 | |
| 331 | // An RAII temporary file class that automatically removes a temporary file. |
| 332 | namespace { |
| 333 | class TemporaryFile { |
| 334 | public: |
| 335 | TemporaryFile(COFFLinkerContext &ctx, StringRef prefix, StringRef extn, |
| 336 | StringRef contents = "" ) |
| 337 | : ctx(ctx) { |
| 338 | SmallString<128> s; |
| 339 | if (auto ec = sys::fs::createTemporaryFile(Prefix: "lld-" + prefix, Suffix: extn, ResultPath&: s)) |
| 340 | Fatal(ctx) << "cannot create a temporary file: " << ec.message(); |
| 341 | path = std::string(s); |
| 342 | |
| 343 | if (!contents.empty()) { |
| 344 | std::error_code ec; |
| 345 | raw_fd_ostream os(path, ec, sys::fs::OF_None); |
| 346 | if (ec) |
| 347 | Fatal(ctx) << "failed to open " << path << ": " << ec.message(); |
| 348 | os << contents; |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | TemporaryFile(TemporaryFile &&obj) noexcept : ctx(obj.ctx) { |
| 353 | std::swap(lhs&: path, rhs&: obj.path); |
| 354 | } |
| 355 | |
| 356 | ~TemporaryFile() { |
| 357 | if (path.empty()) |
| 358 | return; |
| 359 | if (sys::fs::remove(path)) |
| 360 | Fatal(ctx) << "failed to remove " << path; |
| 361 | } |
| 362 | |
| 363 | // Returns a memory buffer of this temporary file. |
| 364 | // Note that this function does not leave the file open, |
| 365 | // so it is safe to remove the file immediately after this function |
| 366 | // is called (you cannot remove an opened file on Windows.) |
| 367 | std::unique_ptr<MemoryBuffer> getMemoryBuffer() { |
| 368 | // IsVolatile=true forces MemoryBuffer to not use mmap(). |
| 369 | return CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, |
| 370 | /*RequiresNullTerminator=*/false, |
| 371 | /*IsVolatile=*/true), |
| 372 | "could not open " + path); |
| 373 | } |
| 374 | |
| 375 | COFFLinkerContext &ctx; |
| 376 | std::string path; |
| 377 | }; |
| 378 | } |
| 379 | |
| 380 | std::string LinkerDriver::createDefaultXml() { |
| 381 | std::string ret; |
| 382 | raw_string_ostream os(ret); |
| 383 | |
| 384 | // Emit the XML. Note that we do *not* verify that the XML attributes are |
| 385 | // syntactically correct. This is intentional for link.exe compatibility. |
| 386 | os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" |
| 387 | << "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n" |
| 388 | << " manifestVersion=\"1.0\">\n" ; |
| 389 | if (ctx.config.manifestUAC) { |
| 390 | os << " <trustInfo>\n" |
| 391 | << " <security>\n" |
| 392 | << " <requestedPrivileges>\n" |
| 393 | << " <requestedExecutionLevel level=" << ctx.config.manifestLevel |
| 394 | << " uiAccess=" << ctx.config.manifestUIAccess << "/>\n" |
| 395 | << " </requestedPrivileges>\n" |
| 396 | << " </security>\n" |
| 397 | << " </trustInfo>\n" ; |
| 398 | } |
| 399 | for (auto manifestDependency : ctx.config.manifestDependencies) { |
| 400 | os << " <dependency>\n" |
| 401 | << " <dependentAssembly>\n" |
| 402 | << " <assemblyIdentity " << manifestDependency << " />\n" |
| 403 | << " </dependentAssembly>\n" |
| 404 | << " </dependency>\n" ; |
| 405 | } |
| 406 | os << "</assembly>\n" ; |
| 407 | return ret; |
| 408 | } |
| 409 | |
| 410 | std::string |
| 411 | LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) { |
| 412 | std::unique_ptr<MemoryBuffer> defaultXmlCopy = |
| 413 | MemoryBuffer::getMemBufferCopy(InputData: defaultXml); |
| 414 | |
| 415 | windows_manifest::WindowsManifestMerger merger; |
| 416 | if (auto e = merger.merge(Manifest: *defaultXmlCopy)) |
| 417 | Fatal(ctx) << "internal manifest tool failed on default xml: " |
| 418 | << toString(E: std::move(e)); |
| 419 | |
| 420 | for (StringRef filename : ctx.config.manifestInput) { |
| 421 | std::unique_ptr<MemoryBuffer> manifest = |
| 422 | check(e: MemoryBuffer::getFile(Filename: filename)); |
| 423 | // Call takeBuffer to include in /reproduce: output if applicable. |
| 424 | if (auto e = merger.merge(Manifest: takeBuffer(mb: std::move(manifest)))) |
| 425 | Fatal(ctx) << "internal manifest tool failed on file " << filename << ": " |
| 426 | << toString(E: std::move(e)); |
| 427 | } |
| 428 | |
| 429 | return std::string(merger.getMergedManifest()->getBuffer()); |
| 430 | } |
| 431 | |
| 432 | std::string |
| 433 | LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) { |
| 434 | // Create the default manifest file as a temporary file. |
| 435 | TemporaryFile Default(ctx, "defaultxml" , "manifest" ); |
| 436 | std::error_code ec; |
| 437 | raw_fd_ostream os(Default.path, ec, sys::fs::OF_TextWithCRLF); |
| 438 | if (ec) |
| 439 | Fatal(ctx) << "failed to open " << Default.path << ": " << ec.message(); |
| 440 | os << defaultXml; |
| 441 | os.close(); |
| 442 | |
| 443 | // Merge user-supplied manifests if they are given. Since libxml2 is not |
| 444 | // enabled, we must shell out to Microsoft's mt.exe tool. |
| 445 | TemporaryFile user(ctx, "user" , "manifest" ); |
| 446 | |
| 447 | Executor e("mt.exe" ); |
| 448 | e.add(s: "/manifest" ); |
| 449 | e.add(s&: Default.path); |
| 450 | for (StringRef filename : ctx.config.manifestInput) { |
| 451 | e.add(s: "/manifest" ); |
| 452 | e.add(s: filename); |
| 453 | |
| 454 | // Manually add the file to the /reproduce: tar if needed. |
| 455 | if (tar) |
| 456 | if (auto mbOrErr = MemoryBuffer::getFile(Filename: filename)) |
| 457 | takeBuffer(mb: std::move(*mbOrErr)); |
| 458 | } |
| 459 | e.add(s: "/nologo" ); |
| 460 | e.add(s: "/out:" + StringRef(user.path)); |
| 461 | e.run(); |
| 462 | |
| 463 | return std::string( |
| 464 | CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path) |
| 465 | .get() |
| 466 | ->getBuffer()); |
| 467 | } |
| 468 | |
| 469 | std::string LinkerDriver::createManifestXml() { |
| 470 | std::string defaultXml = createDefaultXml(); |
| 471 | if (ctx.config.manifestInput.empty()) |
| 472 | return defaultXml; |
| 473 | |
| 474 | if (windows_manifest::isAvailable()) |
| 475 | return createManifestXmlWithInternalMt(defaultXml); |
| 476 | |
| 477 | return createManifestXmlWithExternalMt(defaultXml); |
| 478 | } |
| 479 | |
| 480 | std::unique_ptr<WritableMemoryBuffer> |
| 481 | LinkerDriver::createMemoryBufferForManifestRes(size_t manifestSize) { |
| 482 | size_t resSize = alignTo( |
| 483 | Value: object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + |
| 484 | sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + |
| 485 | sizeof(object::WinResHeaderSuffix) + manifestSize, |
| 486 | Align: object::WIN_RES_DATA_ALIGNMENT); |
| 487 | return WritableMemoryBuffer::getNewMemBuffer(Size: resSize, BufferName: ctx.config.outputFile + |
| 488 | ".manifest.res" ); |
| 489 | } |
| 490 | |
| 491 | static void (char *&buf) { |
| 492 | memcpy(dest: buf, src: COFF::WinResMagic, n: sizeof(COFF::WinResMagic)); |
| 493 | buf += sizeof(COFF::WinResMagic); |
| 494 | memset(s: buf, c: 0, n: object::WIN_RES_NULL_ENTRY_SIZE); |
| 495 | buf += object::WIN_RES_NULL_ENTRY_SIZE; |
| 496 | } |
| 497 | |
| 498 | static void (char *&buf, size_t manifestSize, |
| 499 | int manifestID) { |
| 500 | // Write the prefix. |
| 501 | auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf); |
| 502 | prefix->DataSize = manifestSize; |
| 503 | prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + |
| 504 | sizeof(object::WinResIDs) + |
| 505 | sizeof(object::WinResHeaderSuffix); |
| 506 | buf += sizeof(object::WinResHeaderPrefix); |
| 507 | |
| 508 | // Write the Type/Name IDs. |
| 509 | auto *iDs = reinterpret_cast<object::WinResIDs *>(buf); |
| 510 | iDs->setType(RT_MANIFEST); |
| 511 | iDs->setName(manifestID); |
| 512 | buf += sizeof(object::WinResIDs); |
| 513 | |
| 514 | // Write the suffix. |
| 515 | auto *suffix = reinterpret_cast<object::WinResHeaderSuffix *>(buf); |
| 516 | suffix->DataVersion = 0; |
| 517 | suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; |
| 518 | suffix->Language = SUBLANG_ENGLISH_US; |
| 519 | suffix->Version = 0; |
| 520 | suffix->Characteristics = 0; |
| 521 | buf += sizeof(object::WinResHeaderSuffix); |
| 522 | } |
| 523 | |
| 524 | // Create a resource file containing a manifest XML. |
| 525 | std::unique_ptr<MemoryBuffer> LinkerDriver::createManifestRes() { |
| 526 | std::string manifest = createManifestXml(); |
| 527 | |
| 528 | std::unique_ptr<WritableMemoryBuffer> res = |
| 529 | createMemoryBufferForManifestRes(manifestSize: manifest.size()); |
| 530 | |
| 531 | char *buf = res->getBufferStart(); |
| 532 | writeResFileHeader(buf); |
| 533 | writeResEntryHeader(buf, manifestSize: manifest.size(), manifestID: ctx.config.manifestID); |
| 534 | |
| 535 | // Copy the manifest data into the .res file. |
| 536 | std::copy(first: manifest.begin(), last: manifest.end(), result: buf); |
| 537 | return std::move(res); |
| 538 | } |
| 539 | |
| 540 | void LinkerDriver::createSideBySideManifest() { |
| 541 | std::string path = std::string(ctx.config.manifestFile); |
| 542 | if (path == "" ) |
| 543 | path = ctx.config.outputFile + ".manifest" ; |
| 544 | std::error_code ec; |
| 545 | raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF); |
| 546 | if (ec) |
| 547 | Fatal(ctx) << "failed to create manifest: " << ec.message(); |
| 548 | out << createManifestXml(); |
| 549 | } |
| 550 | |
| 551 | // Parse a string in the form of |
| 552 | // "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]" |
| 553 | // or "<name>=<dllname>.<name>". |
| 554 | // Used for parsing /export arguments. |
| 555 | Export LinkerDriver::parseExport(StringRef arg) { |
| 556 | Export e; |
| 557 | e.source = ExportSource::Export; |
| 558 | |
| 559 | StringRef rest; |
| 560 | std::tie(args&: e.name, args&: rest) = arg.split(Separator: "," ); |
| 561 | if (e.name.empty()) |
| 562 | goto err; |
| 563 | |
| 564 | if (e.name.contains(C: '=')) { |
| 565 | auto [x, y] = e.name.split(Separator: "=" ); |
| 566 | |
| 567 | // If "<name>=<dllname>.<name>". |
| 568 | if (y.contains(Other: "." )) { |
| 569 | e.name = x; |
| 570 | e.forwardTo = y; |
| 571 | } else { |
| 572 | e.extName = x; |
| 573 | e.name = y; |
| 574 | if (e.name.empty()) |
| 575 | goto err; |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | // Optional parameters |
| 580 | // "[,@ordinal[,NONAME]][,DATA][,PRIVATE][,EXPORTAS,exportname]" |
| 581 | while (!rest.empty()) { |
| 582 | StringRef tok; |
| 583 | std::tie(args&: tok, args&: rest) = rest.split(Separator: "," ); |
| 584 | if (tok.equals_insensitive(RHS: "noname" )) { |
| 585 | if (e.ordinal == 0) |
| 586 | goto err; |
| 587 | e.noname = true; |
| 588 | continue; |
| 589 | } |
| 590 | if (tok.equals_insensitive(RHS: "data" )) { |
| 591 | e.data = true; |
| 592 | continue; |
| 593 | } |
| 594 | if (tok.equals_insensitive(RHS: "constant" )) { |
| 595 | e.constant = true; |
| 596 | continue; |
| 597 | } |
| 598 | if (tok.equals_insensitive(RHS: "private" )) { |
| 599 | e.isPrivate = true; |
| 600 | continue; |
| 601 | } |
| 602 | if (tok.equals_insensitive(RHS: "exportas" )) { |
| 603 | if (!rest.empty() && !rest.contains(C: ',')) |
| 604 | e.exportAs = rest; |
| 605 | else |
| 606 | Err(ctx) << "invalid EXPORTAS value: " << rest; |
| 607 | break; |
| 608 | } |
| 609 | if (tok.starts_with(Prefix: "@" )) { |
| 610 | int32_t ord; |
| 611 | if (tok.substr(Start: 1).getAsInteger(Radix: 0, Result&: ord)) |
| 612 | goto err; |
| 613 | if (ord <= 0 || 65535 < ord) |
| 614 | goto err; |
| 615 | e.ordinal = ord; |
| 616 | continue; |
| 617 | } |
| 618 | goto err; |
| 619 | } |
| 620 | return e; |
| 621 | |
| 622 | err: |
| 623 | Fatal(ctx) << "invalid /export: " << arg; |
| 624 | llvm_unreachable("" ); |
| 625 | } |
| 626 | |
| 627 | // Parses a string in the form of "key=value" and check |
| 628 | // if value matches previous values for the same key. |
| 629 | void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) { |
| 630 | auto [k, v] = arg.split(Separator: '='); |
| 631 | if (k.empty() || v.empty()) |
| 632 | Fatal(ctx) << "/failifmismatch: invalid argument: " << arg; |
| 633 | std::pair<StringRef, InputFile *> existing = ctx.config.mustMatch[k]; |
| 634 | if (!existing.first.empty() && v != existing.first) { |
| 635 | std::string sourceStr = source ? toString(file: source) : "cmd-line" ; |
| 636 | std::string existingStr = |
| 637 | existing.second ? toString(file: existing.second) : "cmd-line" ; |
| 638 | Fatal(ctx) << "/failifmismatch: mismatch detected for '" << k << "':\n>>> " |
| 639 | << existingStr << " has value " << existing.first << "\n>>> " |
| 640 | << sourceStr << " has value " << v; |
| 641 | } |
| 642 | ctx.config.mustMatch[k] = {v, source}; |
| 643 | } |
| 644 | |
| 645 | // Convert Windows resource files (.res files) to a .obj file. |
| 646 | // Does what cvtres.exe does, but in-process and cross-platform. |
| 647 | MemoryBufferRef LinkerDriver::convertResToCOFF(ArrayRef<MemoryBufferRef> mbs, |
| 648 | ArrayRef<ObjFile *> objs) { |
| 649 | object::WindowsResourceParser parser(/* MinGW */ ctx.config.mingw); |
| 650 | |
| 651 | std::vector<std::string> duplicates; |
| 652 | for (MemoryBufferRef mb : mbs) { |
| 653 | std::unique_ptr<object::Binary> bin = check(e: object::createBinary(Source: mb)); |
| 654 | object::WindowsResource *rf = dyn_cast<object::WindowsResource>(Val: bin.get()); |
| 655 | if (!rf) |
| 656 | Fatal(ctx) << "cannot compile non-resource file as resource" ; |
| 657 | |
| 658 | if (auto ec = parser.parse(WR: rf, Duplicates&: duplicates)) |
| 659 | Fatal(ctx) << toString(E: std::move(ec)); |
| 660 | } |
| 661 | |
| 662 | // Note: This processes all .res files before all objs. Ideally they'd be |
| 663 | // handled in the same order they were linked (to keep the right one, if |
| 664 | // there are duplicates that are tolerated due to forceMultipleRes). |
| 665 | for (ObjFile *f : objs) { |
| 666 | object::ResourceSectionRef rsf; |
| 667 | if (auto ec = rsf.load(O: f->getCOFFObj())) |
| 668 | Fatal(ctx) << toString(file: f) << ": " << toString(E: std::move(ec)); |
| 669 | |
| 670 | if (auto ec = parser.parse(RSR&: rsf, Filename: f->getName(), Duplicates&: duplicates)) |
| 671 | Fatal(ctx) << toString(E: std::move(ec)); |
| 672 | } |
| 673 | |
| 674 | if (ctx.config.mingw) |
| 675 | parser.cleanUpManifests(Duplicates&: duplicates); |
| 676 | |
| 677 | for (const auto &dupeDiag : duplicates) |
| 678 | if (ctx.config.forceMultipleRes) |
| 679 | Warn(ctx) << dupeDiag; |
| 680 | else |
| 681 | Err(ctx) << dupeDiag; |
| 682 | |
| 683 | Expected<std::unique_ptr<MemoryBuffer>> e = |
| 684 | llvm::object::writeWindowsResourceCOFF(MachineType: ctx.config.machine, Parser: parser, |
| 685 | TimeDateStamp: ctx.config.timestamp); |
| 686 | if (!e) |
| 687 | Fatal(ctx) << "failed to write .res to COFF: " << toString(E: e.takeError()); |
| 688 | |
| 689 | MemoryBufferRef mbref = **e; |
| 690 | make<std::unique_ptr<MemoryBuffer>>(args: std::move(*e)); // take ownership |
| 691 | return mbref; |
| 692 | } |
| 693 | |
| 694 | // Create OptTable |
| 695 | |
| 696 | #define OPTTABLE_STR_TABLE_CODE |
| 697 | #include "Options.inc" |
| 698 | #undef OPTTABLE_STR_TABLE_CODE |
| 699 | |
| 700 | // Create prefix string literals used in Options.td |
| 701 | #define OPTTABLE_PREFIXES_TABLE_CODE |
| 702 | #include "Options.inc" |
| 703 | #undef OPTTABLE_PREFIXES_TABLE_CODE |
| 704 | |
| 705 | // Create table mapping all options defined in Options.td |
| 706 | static constexpr llvm::opt::OptTable::Info infoTable[] = { |
| 707 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
| 708 | #include "Options.inc" |
| 709 | #undef OPTION |
| 710 | }; |
| 711 | |
| 712 | COFFOptTable::COFFOptTable() |
| 713 | : GenericOptTable(OptionStrTable, OptionPrefixesTable, infoTable, true) {} |
| 714 | |
| 715 | // Set color diagnostics according to --color-diagnostics={auto,always,never} |
| 716 | // or --no-color-diagnostics flags. |
| 717 | static void handleColorDiagnostics(COFFLinkerContext &ctx, |
| 718 | opt::InputArgList &args) { |
| 719 | auto *arg = args.getLastArg(Ids: OPT_color_diagnostics, Ids: OPT_color_diagnostics_eq, |
| 720 | Ids: OPT_no_color_diagnostics); |
| 721 | if (!arg) |
| 722 | return; |
| 723 | if (arg->getOption().getID() == OPT_color_diagnostics) { |
| 724 | ctx.e.errs().enable_colors(enable: true); |
| 725 | } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { |
| 726 | ctx.e.errs().enable_colors(enable: false); |
| 727 | } else { |
| 728 | StringRef s = arg->getValue(); |
| 729 | if (s == "always" ) |
| 730 | ctx.e.errs().enable_colors(enable: true); |
| 731 | else if (s == "never" ) |
| 732 | ctx.e.errs().enable_colors(enable: false); |
| 733 | else if (s != "auto" ) |
| 734 | Err(ctx) << "unknown option: --color-diagnostics=" << s; |
| 735 | } |
| 736 | } |
| 737 | |
| 738 | static cl::TokenizerCallback getQuotingStyle(COFFLinkerContext &ctx, |
| 739 | opt::InputArgList &args) { |
| 740 | if (auto *arg = args.getLastArg(Ids: OPT_rsp_quoting)) { |
| 741 | StringRef s = arg->getValue(); |
| 742 | if (s != "windows" && s != "posix" ) |
| 743 | Err(ctx) << "invalid response file quoting: " << s; |
| 744 | if (s == "windows" ) |
| 745 | return cl::TokenizeWindowsCommandLine; |
| 746 | return cl::TokenizeGNUCommandLine; |
| 747 | } |
| 748 | // The COFF linker always defaults to Windows quoting. |
| 749 | return cl::TokenizeWindowsCommandLine; |
| 750 | } |
| 751 | |
| 752 | ArgParser::ArgParser(COFFLinkerContext &c) : ctx(c) {} |
| 753 | |
| 754 | // Parses a given list of options. |
| 755 | opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) { |
| 756 | // Make InputArgList from string vectors. |
| 757 | unsigned missingIndex; |
| 758 | unsigned missingCount; |
| 759 | |
| 760 | // We need to get the quoting style for response files before parsing all |
| 761 | // options so we parse here before and ignore all the options but |
| 762 | // --rsp-quoting and /lldignoreenv. |
| 763 | // (This means --rsp-quoting can't be added through %LINK%.) |
| 764 | opt::InputArgList args = |
| 765 | ctx.optTable.ParseArgs(Args: argv, MissingArgIndex&: missingIndex, MissingArgCount&: missingCount); |
| 766 | |
| 767 | // Expand response files (arguments in the form of @<filename>) and insert |
| 768 | // flags from %LINK% and %_LINK_%, and then parse the argument again. |
| 769 | SmallVector<const char *, 256> expandedArgv(argv.data(), |
| 770 | argv.data() + argv.size()); |
| 771 | if (!args.hasArg(Ids: OPT_lldignoreenv)) |
| 772 | addLINK(argv&: expandedArgv); |
| 773 | cl::ExpandResponseFiles(Saver&: saver(), Tokenizer: getQuotingStyle(ctx, args), Argv&: expandedArgv); |
| 774 | args = ctx.optTable.ParseArgs(Args: ArrayRef(expandedArgv).drop_front(), |
| 775 | MissingArgIndex&: missingIndex, MissingArgCount&: missingCount); |
| 776 | |
| 777 | // Print the real command line if response files are expanded. |
| 778 | if (args.hasArg(Ids: OPT_verbose) && argv.size() != expandedArgv.size()) { |
| 779 | std::string msg = "Command line:" ; |
| 780 | for (const char *s : expandedArgv) |
| 781 | msg += " " + std::string(s); |
| 782 | Msg(ctx) << msg; |
| 783 | } |
| 784 | |
| 785 | // Save the command line after response file expansion so we can write it to |
| 786 | // the PDB if necessary. Mimic MSVC, which skips input files. |
| 787 | ctx.config.argv = {argv[0]}; |
| 788 | for (opt::Arg *arg : args) { |
| 789 | if (arg->getOption().getKind() != opt::Option::InputClass) { |
| 790 | ctx.config.argv.emplace_back(args: args.getArgString(Index: arg->getIndex())); |
| 791 | } |
| 792 | } |
| 793 | |
| 794 | // Handle /WX early since it converts missing argument warnings to errors. |
| 795 | ctx.e.fatalWarnings = args.hasFlag(Pos: OPT_WX, Neg: OPT_WX_no, Default: false); |
| 796 | |
| 797 | if (missingCount) |
| 798 | Fatal(ctx) << args.getArgString(Index: missingIndex) << ": missing argument" ; |
| 799 | |
| 800 | handleColorDiagnostics(ctx, args); |
| 801 | |
| 802 | for (opt::Arg *arg : args.filtered(Ids: OPT_UNKNOWN)) { |
| 803 | std::string nearest; |
| 804 | if (ctx.optTable.findNearest(Option: arg->getAsString(Args: args), NearestString&: nearest) > 1) |
| 805 | Warn(ctx) << "ignoring unknown argument '" << arg->getAsString(Args: args) |
| 806 | << "'" ; |
| 807 | else |
| 808 | Warn(ctx) << "ignoring unknown argument '" << arg->getAsString(Args: args) |
| 809 | << "', did you mean '" << nearest << "'" ; |
| 810 | } |
| 811 | |
| 812 | if (args.hasArg(Ids: OPT_lib)) |
| 813 | Warn(ctx) << "ignoring /lib since it's not the first argument" ; |
| 814 | |
| 815 | return args; |
| 816 | } |
| 817 | |
| 818 | // Tokenizes and parses a given string as command line in .drective section. |
| 819 | ParsedDirectives ArgParser::parseDirectives(StringRef s) { |
| 820 | ParsedDirectives result; |
| 821 | SmallVector<const char *, 16> rest; |
| 822 | |
| 823 | // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for |
| 824 | // potentially every symbol in the object, so they must be handled quickly. |
| 825 | SmallVector<StringRef, 16> tokens; |
| 826 | cl::TokenizeWindowsCommandLineNoCopy(Source: s, Saver&: saver(), NewArgv&: tokens); |
| 827 | for (StringRef tok : tokens) { |
| 828 | if (tok.starts_with_insensitive(Prefix: "/export:" ) || |
| 829 | tok.starts_with_insensitive(Prefix: "-export:" )) |
| 830 | result.exports.push_back(x: tok.substr(Start: strlen(s: "/export:" ))); |
| 831 | else if (tok.starts_with_insensitive(Prefix: "/include:" ) || |
| 832 | tok.starts_with_insensitive(Prefix: "-include:" )) |
| 833 | result.includes.push_back(x: tok.substr(Start: strlen(s: "/include:" ))); |
| 834 | else if (tok.starts_with_insensitive(Prefix: "/exclude-symbols:" ) || |
| 835 | tok.starts_with_insensitive(Prefix: "-exclude-symbols:" )) |
| 836 | result.excludes.push_back(x: tok.substr(Start: strlen(s: "/exclude-symbols:" ))); |
| 837 | else { |
| 838 | // Copy substrings that are not valid C strings. The tokenizer may have |
| 839 | // already copied quoted arguments for us, so those do not need to be |
| 840 | // copied again. |
| 841 | bool HasNul = tok.end() != s.end() && tok.data()[tok.size()] == '\0'; |
| 842 | rest.push_back(Elt: HasNul ? tok.data() : saver().save(S: tok).data()); |
| 843 | } |
| 844 | } |
| 845 | |
| 846 | // Make InputArgList from unparsed string vectors. |
| 847 | unsigned missingIndex; |
| 848 | unsigned missingCount; |
| 849 | |
| 850 | result.args = ctx.optTable.ParseArgs(Args: rest, MissingArgIndex&: missingIndex, MissingArgCount&: missingCount); |
| 851 | |
| 852 | if (missingCount) |
| 853 | Fatal(ctx) << result.args.getArgString(Index: missingIndex) |
| 854 | << ": missing argument" ; |
| 855 | for (auto *arg : result.args.filtered(Ids: OPT_UNKNOWN)) |
| 856 | Warn(ctx) << "ignoring unknown argument: " << arg->getAsString(Args: result.args); |
| 857 | return result; |
| 858 | } |
| 859 | |
| 860 | // link.exe has an interesting feature. If LINK or _LINK_ environment |
| 861 | // variables exist, their contents are handled as command line strings. |
| 862 | // So you can pass extra arguments using them. |
| 863 | void ArgParser::addLINK(SmallVector<const char *, 256> &argv) { |
| 864 | // Concatenate LINK env and command line arguments, and then parse them. |
| 865 | if (std::optional<std::string> s = Process::GetEnv(name: "LINK" )) { |
| 866 | std::vector<const char *> v = tokenize(s: *s); |
| 867 | argv.insert(I: std::next(x: argv.begin()), From: v.begin(), To: v.end()); |
| 868 | } |
| 869 | if (std::optional<std::string> s = Process::GetEnv(name: "_LINK_" )) { |
| 870 | std::vector<const char *> v = tokenize(s: *s); |
| 871 | argv.insert(I: std::next(x: argv.begin()), From: v.begin(), To: v.end()); |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | std::vector<const char *> ArgParser::tokenize(StringRef s) { |
| 876 | SmallVector<const char *, 16> tokens; |
| 877 | cl::TokenizeWindowsCommandLine(Source: s, Saver&: saver(), NewArgv&: tokens); |
| 878 | return std::vector<const char *>(tokens.begin(), tokens.end()); |
| 879 | } |
| 880 | |
| 881 | void LinkerDriver::printHelp(const char *argv0) { |
| 882 | ctx.optTable.printHelp(OS&: ctx.e.outs(), |
| 883 | Usage: (std::string(argv0) + " [options] file..." ).c_str(), |
| 884 | Title: "LLVM Linker" , ShowHidden: false); |
| 885 | } |
| 886 | |
| 887 | } // namespace coff |
| 888 | } // namespace lld |
| 889 | |