Skip to content

Conversation

frobtech
Copy link
Contributor

This makes the UEFI driver's handling of linking more canonical
in a few ways:

  • Use /option:value syntax with lld-link as in the MSVC driver.
  • Handle -nostdlib, -nodefaultlibs, -r and call common
    subroutines when they aren't set. This covers sanitizer and
    profile runtimes and their associated switches; compiler-rt
    builds do not yet provide these libraries, but the driver
    behavior is opt-in and supports all the opt-in/out plumbing
    like other targets do. This lets command lines immediately
    use the opt-out switches even when they are superfluous for
    opt-in features, as build system plumbing often needs to do.

It also updates some TODO comments for how the driver behavior
will look when more runtime support is ready.

This makes the UEFI driver's handling of linking more canonical
in a few ways:
 * Use /option:value syntax with lld-link as in the MSVC driver.
 * Handle -nostdlib, -nodefaultlibs, -r and call common
   subroutines when they aren't set.  This covers sanitizer and
   profile runtimes and their associated switches; compiler-rt
   builds do not yet provide these libraries, but the driver
   behavior is opt-in and supports all the opt-in/out plumbing
   like other targets do.  This lets command lines immediately
   use the opt-out switches even when they are superfluous for
   opt-in features, as build system plumbing often needs to do.

It also updates some TODO comments for how the driver behavior
will look when more runtime support is ready.
@frobtech frobtech marked this pull request as ready for review September 18, 2025 19:45
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Sep 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 18, 2025

@llvm/pr-subscribers-clang-driver

@llvm/pr-subscribers-clang

Author: Roland McGrath (frobtech)

Changes

This makes the UEFI driver's handling of linking more canonical
in a few ways:

  • Use /option:value syntax with lld-link as in the MSVC driver.
  • Handle -nostdlib, -nodefaultlibs, -r and call common
    subroutines when they aren't set. This covers sanitizer and
    profile runtimes and their associated switches; compiler-rt
    builds do not yet provide these libraries, but the driver
    behavior is opt-in and supports all the opt-in/out plumbing
    like other targets do. This lets command lines immediately
    use the opt-out switches even when they are superfluous for
    opt-in features, as build system plumbing often needs to do.

It also updates some TODO comments for how the driver behavior
will look when more runtime support is ready.


Full diff: https://github.com/llvm/llvm-project/pull/159639.diff

2 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/UEFI.cpp (+26-11)
  • (modified) clang/test/Driver/uefi-constructed-args.c (+5-5)
diff --git a/clang/lib/Driver/ToolChains/UEFI.cpp b/clang/lib/Driver/ToolChains/UEFI.cpp
index 2b41173543477..75adbf149197b 100644
--- a/clang/lib/Driver/ToolChains/UEFI.cpp
+++ b/clang/lib/Driver/ToolChains/UEFI.cpp
@@ -57,29 +57,44 @@ void tools::uefi::Linker::ConstructJob(Compilation &C, const JobAction &JA,
   assert((Output.isFilename() || Output.isNothing()) && "invalid output");
   if (Output.isFilename())
     CmdArgs.push_back(
-        Args.MakeArgString(std::string("-out:") + Output.getFilename()));
+        Args.MakeArgString(std::string("/out:") + Output.getFilename()));
 
-  CmdArgs.push_back("-nologo");
-
-  // TODO: Other UEFI binary subsystems that are currently unsupported:
-  // efi_boot_service_driver, efi_rom, efi_runtime_driver.
-  CmdArgs.push_back("-subsystem:efi_application");
+  CmdArgs.push_back("/nologo");
 
   // Default entry function name according to the TianoCore reference
-  // implementation is EfiMain.
-  // TODO: Provide a flag to override the entry function name.
-  CmdArgs.push_back("-entry:EfiMain");
+  // implementation is EfiMain.  -Wl,/subsystem:... or -Wl,/entry:... can
+  // override these since they will be added later in AddLinkerInputs.
+  CmdArgs.push_back("/subsystem:efi_application");
+  CmdArgs.push_back("/entry:EfiMain");
 
   // "Terminal Service Aware" flag is not needed for UEFI applications.
-  CmdArgs.push_back("-tsaware:no");
+  CmdArgs.push_back("/tsaware:no");
 
   if (Args.hasArg(options::OPT_g_Group, options::OPT__SLASH_Z7))
-    CmdArgs.push_back("-debug");
+    CmdArgs.push_back("/debug");
 
   Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link);
 
   AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
 
+  // Sample these options first so they are claimed even under -nostdlib et al.
+  bool NoLibc = Args.hasArg(options::OPT_nolibc);
+  if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs,
+                   options::OPT_r)) {
+    addSanitizerRuntimes(TC, Args, CmdArgs);
+
+    addXRayRuntime(TC, Args, CmdArgs);
+
+    TC.addProfileRTLibs(Args, CmdArgs);
+
+    // TODO: When compiler-rt/lib/builtins is ready, enable this call:
+    // AddRunTimeLibs(TC, TC.getDriver(), CmdArgs, Args);
+
+    if (!NoLibc) {
+      // TODO: When there is a libc ready, add it here.
+    }
+  }
+
   // This should ideally be handled by ToolChain::GetLinkerPath but we need
   // to special case some linker paths. In the case of lld, we need to
   // translate 'lld' into 'lld-link'.
diff --git a/clang/test/Driver/uefi-constructed-args.c b/clang/test/Driver/uefi-constructed-args.c
index 49ede47a8953e..c06cce351d654 100644
--- a/clang/test/Driver/uefi-constructed-args.c
+++ b/clang/test/Driver/uefi-constructed-args.c
@@ -7,8 +7,8 @@
 // CHECK-SAME: "-mrelocation-model" "pic" "-pic-level" "2"
 // CHECK-SAME: "-mframe-pointer=all"
 // CHECK-SAME: "-fms-extensions"
-// CHECK-NEXT: "-nologo"
-// CHECK-SAME: "-subsystem:efi_application"
-// CHECK-SAME: "-entry:EfiMain"
-// CHECK-SAME: "-tsaware:no"
-// CHECK-SAME: "-debug"
+// CHECK-NEXT: "/nologo"
+// CHECK-SAME: "/subsystem:efi_application"
+// CHECK-SAME: "/entry:EfiMain"
+// CHECK-SAME: "/tsaware:no"
+// CHECK-SAME: "/debug"

@petrhosek
Copy link
Member

Is there any particular reason for using the /option:value syntax over -option:value given that lld-link supports both?

@frobtech
Copy link
Contributor Author

Is there any particular reason for using the /option:value syntax over -option:value given that lld-link supports both?

It's the common convention for the tool syntax that lld-link is emulating. It's consistent with the MSVC driver in Clang. That makes it easier to compare link lines between the two backends, for example.

It's also possible that the UEFI backend could be made to work with the MSVC linker instead of LLD as with the MSVC backend (if anyone cared to wire it up with -fuse-ld=), and I'm not sure MSVC accepts both forms.

@frobtech frobtech merged commit fc73ef4 into llvm:main Sep 19, 2025
13 checks passed
@frobtech frobtech deleted the p/clang-uefi-driver-link-flags branch September 19, 2025 18:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants