From: Pedro Alves <pedro@palves.net>
To: Eli Zaretskii <eliz@gnu.org>
Cc: gdb-patches@sourceware.org
Subject: [PATCH] gdb/Windows testsuite: Embed asInvoker manifest in test executables
Date: Fri, 10 Jul 2026 19:59:17 +0100 [thread overview]
Message-ID: <17aacd11-cc42-43eb-8328-bdcedcc0dfb5@palves.net> (raw)
In-Reply-To: <86zezzjucz.fsf@gnu.org>
On 2026-07-10 06:18, Eli Zaretskii wrote:
>> From: Pedro Alves <pedro@palves.net>
>> Turns out that Windows has an "installer detection" heuristic that
>> refuses to launch executables whose filename contains keywords like
>> "update", "setup", and "install" without elevation (admin rights),
>> unless the PE embeds a manifest declaring
>> requestedExecutionLevel="asInvoker".
>>
...
>> - Matches the word (e.g. "update") as a substring anywhere in the
>> filename (not just as a prefix).
>>
...
> Note that IME, not only programs literally called "install.exe",
> "update.exe" etc. are blocked, but also programs whose name begins
> with these words. For example, I had problems with the Texinfo's
> install-info.exe.
Right, I mentioned this above. It triggers if the string appears anywhere
on the filename as a substring. E.g., fooupdatebar.exe triggers it.
It's curious that you bring up patch.exe. In the downstream testcase
I mention, the initial comment that I wrote there mentioned "patch" as "bad"
word too (as some docs somewhere mention it), and then after the initial fix,
I noticed that "patch" is actually OK, and so I dropped it from the comment
in a follow up patch.
Just tried it again, to confirm:
$ ./hello.exe
Hello!
$ mv hello.exe update.exe
$ ./update.exe
-bash: /c/msys2/home/alves/update.exe: Permission denied
$ mv update.exe patch.exe
$ ./patch.exe
Hello!
So looks like Microsoft decided that "patch" wasn't a bad word after all
at some point more recently...
> FTR, another way of overcoming this is by providing a manifest file.
> Below is an example of such a manifest I use for patch.exe. The
> manifest must be named PROGRAM.exe.manifest and should be in the same
> directory as the executable.
Indeed, I mentioned "unless manifest" further above too.
Putting a sidecar manifest file next to every executable wouldn't work very well,
as some testcases want to rename the executable after compiling it.
It should also be possible to embed a manifest into the binary directly
using windres. The problem I ran into the last time I tried this was that
windres/llvm-windres wasn't available in the AMD GPU LLVM-based toolchain I need
to support. llvm-rc can also be used (with a different interface), and it is
available in some distributions, but even that one is not available in the latest
daily builds of TheRock for Windows, which I'll need to support.
... several hours of head banging and poking later ...
OK, I got it working. Turns out that lld-link has built-in support for
embedding a manifest! Nice. So I can cover all the bases after all.
Also, I noticed that executables produced by the GCC 16 that comes with
MSYS2 do not have the issue. (??!) Digging a bit, it turns out that both MSYS2 and
Cygwin ship a default-manifest.o object file that GCC pulls in via a spec file.
This default-manifest.o file is in a separate optional package, which may be
removed, and GCC keeps working, just won't link in the default manifest.
https://gcc.gnu.org/legacy-ml/gcc-patches/2014-04/msg01378.html
https://sourceforge.net/p/mingw-w64/wiki2/default_manifest/
I'm normally using the xPack GCC distribution (https://github.com/xpack-dev-tools/gcc-xpack/),
which does have that .o file, and thus no default manifest.
As for the gdb.rocm/ testcase I saw this first on, that is compiled wit hipcc/llvm/lld-link,
which likewise has no manifest by default.
Find below the new patch adding a manifest to every executable in the testsuite. WDYT of this one?
Note: this caches the windres .o file using the same logic as for set_unbuffered_mode.o, which in
parallel mode should hit the same EBUSY failure fixed by the file_rename_atomic proc added by:
https://inbox.sourceware.org/gdb-patches/20260709155657.412594-1-pedro@palves.net/T/#u
Funny I wrote there "Put the rename in
its own procedure, as I expect this will be used in more places.", and then immediately
run into such a spot the day after. I'll tweak this new code to use that other patch's
proc once that one is in, it's just a one line change.
-- >8 --
From d721c058e8c31a07d1ce188938df8707ef773e04 Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Fri, 10 Jul 2026 12:28:34 +0100
Subject: [PATCH] gdb/Windows testsuite: Embed asInvoker manifest in test
executables
Running gdb.base/execl-update-breakpoints.exp on Windows 11 shows this
FAIL:
(gdb) run
Starting program: .../execl-update-breakpoints1.exe
Error creating process .../execl-update-breakpoints1.exe (error 740): The requested operation requires elevation.
(gdb) FAIL: gdb.base/execl-update-breakpoints.exp: runto: run to main
Error 740 is ERROR_ELEVATION_REQUIRED.
Windows has an "installer detection" heuristic that refuses to launch
executables whose file name contains keywords like "update", "setup"
and "install" without elevation (admin rights), unless the PE embeds
an application manifest declaring requestedExecutionLevel="asInvoker".
Some older Microsoft documentation claims the heuristic only applies
to 32-bit binaries, but what I observe is that:
- It triggers with 64-bit PEs on current Windows.
And also:
- It matches the word (e.g. "update") as a substring anywhere in the
file name, not just as a prefix.
- It is not drive-dependent. I thought moving the executable to a
dev drive might suppress the check, but it does not.
I saw this problem first in a downstream ROCgdb testcase, and there I
worked around it by renaming that particular testcase. This is the
second case now, so rather than teach individual testcases to avoid
the "bad" words, fix it once, centrally, in a way that is independent
of the executable's file name.
The Microsoft-sanctioned escape hatch is to embed an application
manifest that declares the "asInvoker" execution level. That's what
this commit does, it makes gdb_compile embed one in every Windows
executable it builds.
How the manifest gets embedded depends on the linker. There are two
ways:
- GNU ld can't embed a manifest by itself, so compile it into a
resource object with windres and link that in.
- lld-link is able to embed one directly, with the /manifest:embed
and /manifestinput options.
Since it's not guaranteed that clang always links with lld-link, and
conversely, gcc may also link with lld-link, gdb_compile picks between
the two by probing what the linker accepts, rather than keying off the
compiler or the target.
This whole issue only reproduces with some toolchains, because some
MinGW or Cygwin installations already embed an equivalent manifest of
their own, via a default-manifest.o that the gcc spec links in. That
object comes from the separate windows-default-manifest package, so
whether it is embedded depends on the installation rather than the gcc
version.
Since we're adding a manifest, might as well declare the supported
Windows versions there too (a compatibility section listing
per-version GUIDs), like default-manifest.o does. Without those, the
version-reporting APIs (GetVersionEx and friends) cap out at Windows
8. We should probably add such a manifest to GDB itself too, at some
point.
Change-Id: Ic0afc925136a61c259cb8b6681627dc1775a8445
---
gdb/testsuite/lib/future.exp | 10 ++
gdb/testsuite/lib/gdb.exp | 150 +++++++++++++++++++++++++++++
gdb/testsuite/lib/windows.manifest | 53 ++++++++++
gdb/testsuite/lib/windows.rc | 23 +++++
4 files changed, 236 insertions(+)
create mode 100644 gdb/testsuite/lib/windows.manifest
create mode 100644 gdb/testsuite/lib/windows.rc
diff --git a/gdb/testsuite/lib/future.exp b/gdb/testsuite/lib/future.exp
index 0f45aa44628..3ab160a05fc 100644
--- a/gdb/testsuite/lib/future.exp
+++ b/gdb/testsuite/lib/future.exp
@@ -177,6 +177,16 @@ proc gdb_find_readelf {} {
return $readelf
}
+proc gdb_find_windres {} {
+ global WINDRES_FOR_TARGET
+ if {[info exists WINDRES_FOR_TARGET]} {
+ set windres $WINDRES_FOR_TARGET
+ } else {
+ set windres [transform windres]
+ }
+ return $windres
+}
+
proc gdb_find_eu-unstrip {} {
global EU_UNSTRIP_FOR_TARGET
if {[info exists EU_UNSTRIP_FOR_TARGET]} {
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index a40c87c6727..9b86d53be08 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -4217,6 +4217,12 @@ proc is_aarch64_target {} {
return [expr {![is_aarch32_target]}]
}
+# Return true if the target is Windows-based.
+
+proc is_windows_based_target {} {
+ return [expr {[istarget *-*-cygwin*] || [istarget *-*-mingw*]}]
+}
+
# Return 1 if displaced stepping is supported on target, otherwise, return 0.
proc support_displaced_stepping {} {
@@ -6402,6 +6408,97 @@ proc quote_for_host { args } {
return $str
}
+# Set while linker_supports_manifest_embed is running its test link,
+# so that the inner gdb_compile that link goes through skips the
+# manifest-embedding logic and doesn't recurse back into the probe.
+set gdb_probing_manifest_embed 0
+
+# Return the ldflags (as a list of "ldflags=..." options) that make
+# lld-link embed our application manifest into the executable.
+
+proc gdb_windows_manifest_embed_lld_link_ldflags {} {
+ global srcdir
+
+ set ldflags {}
+
+ # Turn on embedding (off by default).
+ lappend ldflags ldflags=-Wl,/manifest:embed
+
+ # Stop lld-link from also generating its own UAC block. With
+ # this, lld-link embeds our input as-is, while without it lld-link
+ # runs its manifest merger, which before LLVM 21 reprefixes
+ # trustInfo into the asm.v1 namespace and produces a manifest the
+ # Windows loader rejects. See
+ # <https://github.com/llvm/llvm-project/issues/120394>.
+ lappend ldflags ldflags=-Wl,/manifestuac:no
+
+ # Embed our manifest file. Pass it in Windows-native form.
+ # MSYS2's argument conversion treats a "/foo:/bar" argument as a
+ # colon-separated list of POSIX paths and mistakenly rewrites it
+ # to a semicolon-separated list of Windows paths. E.g.:
+ #
+ # "/manifestinput:/c/gdb/.../windows.manifest"
+ # =>
+ # "C:\msys64\manifestinput;C:\gdb\...\windows.manifest"
+ #
+ # I.e., the flag name itself gets converted as if it were a path,
+ # and the ":" becomes ";".
+ #
+ # What triggers the conversion is the value after the colon looking
+ # like an absolute POSIX path (a leading "/"). "/manifest:embed"
+ # above is left alone because "embed" doesn't. Passing the value
+ # as a native "C:/..." path likewise avoids it.
+ set manifest [host_file_normalize ${srcdir}/lib/windows.manifest]
+ lappend ldflags ldflags=-Wl,/manifestinput:${manifest}
+
+ return $ldflags
+}
+
+# Compile lib/windows.rc into an object embedding our application
+# manifest and return the object path, so that gdb_compile can link it
+# into every Windows test executable. The result is cached. Returns
+# the empty string on failure. This is used when linking with GNU ld,
+# which cannot embed a manifest by itself.
+
+proc gdb_windows_manifest_obj {} {
+ global srcdir objdir
+ global gdb_saved_windows_manifest_obj
+
+ if {[info exists gdb_saved_windows_manifest_obj]} {
+ return $gdb_saved_windows_manifest_obj
+ }
+
+ set rc_src ${srcdir}/lib/windows.rc
+ set obj_basename windows-manifest.o
+ set obj [standard_temp_file $obj_basename]
+
+ set windres [gdb_find_windres]
+ set cmd [list $windres -I [file dirname $rc_src] \
+ -i $rc_src -o $obj -O coff]
+ verbose -log "Executing $cmd"
+ if {[catch {exec {*}$cmd} output]} {
+ verbose -log "gdb_windows_manifest_obj: windres failed: $output"
+ return ""
+ }
+
+ if {[is_remote host]} {
+ set saved $obj_basename
+ } else {
+ set saved ${objdir}/$obj_basename
+ }
+ # Link a copy of the output object, because the original may be
+ # automatically deleted.
+ if {[info exists ::GDB_PARALLEL]} {
+ # Make sure to write the .o file atomically. (Note
+ # GDB_PARALLEL mode does not support remote host testing.)
+ file rename -force -- $obj $saved
+ } else {
+ remote_download host $obj $saved
+ }
+ set gdb_saved_windows_manifest_obj $saved
+ return $saved
+}
+
# Compile source files specified by SOURCE into a binary of type TYPE at path
# DEST. gdb_compile is implemented using DejaGnu's target_compile, so the type
# parameter and most options are passed directly to it.
@@ -6942,6 +7039,40 @@ proc gdb_compile {source dest type options} {
}
}
+ # On Windows, embed an "asInvoker" application manifest, so that
+ # Windows doesn't refuse to launch executables (with
+ # ERROR_ELEVATION_REQUIRED/740) whose file name happens to contain
+ # an installer-detection keyword such as "update", "setup" or
+ # "install".
+ #
+ # There are two ways to get the manifest in, depending on the
+ # linker:
+ #
+ # - GNU ld can't embed a manifest by itself, so compile the
+ # manifest into a resource object with windres and link that
+ # in.
+ #
+ # - lld-link can embed a manifest by itself, no resource compiler
+ # needed. Note that the LLVM toolchain has llvm-windres and
+ # llvm-rc, but not all LLVM-based toolchain distributions ship
+ # them.
+ #
+ # Probe whether the linker supports embedding a manifest rather
+ # than trying to guess which linker is in use from the compiler or
+ # target.
+ if { $type == "executable"
+ && [is_windows_based_target]
+ && !$::gdb_probing_manifest_embed } {
+ if { [linker_supports_manifest_embed] } {
+ lappend options {*}[gdb_windows_manifest_embed_lld_link_ldflags]
+ } else {
+ set manifest_obj [gdb_windows_manifest_obj]
+ if { $manifest_obj != "" } {
+ lappend options "ldflags=$manifest_obj"
+ }
+ }
+ }
+
# Automatically handle includes in testsuite/lib/.
auto_lappend_include_files options $source
@@ -11203,6 +11334,25 @@ gdb_caching_proc linker_supports_image_base_flag {} {
return [gdb_simple_compile $me $src executable $flags]
}
+# Return 1 if the linker is lld-link which can embed our application
+# manifest by itself, otherwise 0. Probes the exact flag combination
+# gdb_compile uses.
+gdb_caching_proc linker_supports_manifest_embed {} {
+ set me "linker_supports_manifest_embed"
+ set flags [gdb_windows_manifest_embed_lld_link_ldflags]
+ set src { int main() { return 0; } }
+
+ # Guard against infinite recursion: the test link below itself
+ # goes through gdb_compile, which consults this proc to decide
+ # whether to embed the manifest. The guard makes that inner
+ # gdb_compile skip the manifest logic.
+ set ::gdb_probing_manifest_embed 1
+ set result [gdb_simple_compile $me $src executable $flags]
+ set ::gdb_probing_manifest_embed 0
+
+ return $result
+}
+
# Return 1 if compiler supports scalar_storage_order attribute, otherwise
# return 0.
diff --git a/gdb/testsuite/lib/windows.manifest b/gdb/testsuite/lib/windows.manifest
new file mode 100644
index 00000000000..f0e371588f2
--- /dev/null
+++ b/gdb/testsuite/lib/windows.manifest
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<!-- Windows' installer-detection heuristic refuses to launch an
+ executable whose file name contains words such as "update",
+ "setup" or "install" unless the PE embeds an application manifest
+ that declares the "asInvoker" execution level. gdb_compile embeds
+ this manifest in every test executable: with GNU ld via the
+ windows.rc resource script, with lld-link via
+ /manifest:embed /manifestinput. See gdb_compile in lib/gdb.exp.
+
+ The compatibility section declares the supported Windows versions.
+ Without it, the version-reporting APIs (GetVersionEx and friends)
+ cap out at Windows 8. -->
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 10 and Windows 11 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ </application>
+ </compatibility>
+</assembly>
diff --git a/gdb/testsuite/lib/windows.rc b/gdb/testsuite/lib/windows.rc
new file mode 100644
index 00000000000..2c1b2ca81d4
--- /dev/null
+++ b/gdb/testsuite/lib/windows.rc
@@ -0,0 +1,23 @@
+/* Copyright (C) 2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Embed the application manifest.
+
+ 1 => CREATEPROCESS_MANIFEST_RESOURCE_ID
+ 24 => RT_MANIFEST
+*/
+1 24 "windows.manifest"
base-commit: 490469846dcef89fe53668bdbba73591c64bed61
--
2.54.0
next prev parent reply other threads:[~2026-07-10 18:59 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 17:43 [PATCH] gdb/Windows testsuite: Avoid "bad" words in exe names Pedro Alves
2026-07-10 5:18 ` Eli Zaretskii
2026-07-10 18:59 ` Pedro Alves [this message]
2026-07-10 19:09 ` [PATCH] gdb/Windows testsuite: Embed asInvoker manifest in test executables Pedro Alves
2026-07-11 6:07 ` Eli Zaretskii
2026-07-22 14:16 ` Pedro Alves
2026-07-10 14:41 ` [PATCH] gdb/Windows testsuite: Avoid "bad" words in exe names Andrew Burgess
2026-07-10 19:14 ` Pedro Alves
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=17aacd11-cc42-43eb-8328-bdcedcc0dfb5@palves.net \
--to=pedro@palves.net \
--cc=eliz@gnu.org \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox