[gcc r17-3189] PE/COFF: Implement visibility attribute via .drectve

Jonathan Yong via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:baa4e4ddf800602d818470ef493d3508c4e1e32a

commit r17-3189-gbaa4e4ddf800602d818470ef493d3508c4e1e32a
Author: Oleg Tolmatcev <[email protected]>
Date:   Sun Aug 9 16:41:31 2026 +0200

    PE/COFF: Implement visibility attribute via .drectve
    
    On PE/COFF targets (MinGW, Cygwin), the visibility attribute was
    previously ignored with a warning.  This patch makes it functional
    by emitting -exclude-symbols directives into the .drectve section,
    matching Clang's behavior.  The GNU linker already reads and
    respects these directives during auto-export, so hidden/internal
    symbols are now correctly excluded from DLL exports.
    
    gcc/ChangeLog:
    
            * config/mingw/winnt.cc (i386_pe_drectve_name): New.
            (i386_pe_assemble_visibility): Emit -exclude-symbols
            directives into .drectve for VISIBILITY_HIDDEN and
            VISIBILITY_INTERNAL instead of warning.  Use the external
            symbol spelling for 32-bit PE names.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/i386/visibility-hidden-mingw.c: New test.
            * gcc.target/i386/visibility-hidden-mingw-32.c: New test.
    
    Signed-off-by: Oleg Tolmatcev <[email protected]>
    Signed-off-by: Jonathan Yong <[email protected]>

Diff:
---
 gcc/config/mingw/winnt.cc                          | 38 +++++++++++++++++-----
 .../gcc.target/i386/visibility-hidden-mingw-32.c   | 12 +++++++
 .../gcc.target/i386/visibility-hidden-mingw.c      | 24 ++++++++++++++
 3 files changed, 66 insertions(+), 8 deletions(-)

diff --git a/gcc/config/mingw/winnt.cc b/gcc/config/mingw/winnt.cc
index 66d7450652d2..2e06b0e81462 100644
--- a/gcc/config/mingw/winnt.cc
+++ b/gcc/config/mingw/winnt.cc
@@ -254,20 +254,42 @@ i386_pe_maybe_mangle_decl_assembler_name (tree decl, tree id)
 
 #endif
 
+/* Return the symbol spelling used by .drectve exclude-symbols directives.
+   This is the external name without the user label prefix, while preserving
+   calling-convention decoration such as fastcall's leading '@' or a
+   stdcall suffix.  */
+
+static const char *
+i386_pe_drectve_name (tree id)
+{
+  const char *name = targetm.strip_name_encoding (IDENTIFIER_POINTER (id));
+  size_t prefix_len = strlen (user_label_prefix);
+
+  if (prefix_len != 0
+      && strncmp (name, user_label_prefix, prefix_len) == 0)
+    name += prefix_len;
+
+  return name;
+}
+
 /* Emit an assembler directive to set symbol for DECL visibility to
    the visibility type VIS, which must not be VISIBILITY_DEFAULT.
-   As for PE there is no hidden support in gas, we just warn for
-   user-specified visibility attributes.  */
+   Emit a -exclude-symbols directive into .drectve, compatible with
+   what Clang emits for hidden visibility on PE/COFF.  */
 
 void
-i386_pe_assemble_visibility (tree decl, int)
+i386_pe_assemble_visibility (tree decl, int vis)
 {
-  if (!decl
-      || !lookup_attribute ("visibility", DECL_ATTRIBUTES (decl)))
+  if (!decl)
     return;
-  if (!DECL_ARTIFICIAL (decl))
-    warning (OPT_Wattributes, "visibility attribute not supported "
-			      "in this configuration; ignored");
+
+  if (vis == VISIBILITY_HIDDEN || vis == VISIBILITY_INTERNAL)
+    {
+      tree id = DECL_ASSEMBLER_NAME (decl);
+      const char *name = i386_pe_drectve_name (id);
+      drectve_section ();
+      fprintf (asm_out_file, "\t.ascii \" -exclude-symbols:%s\"\n", name);
+    }
 }
 
 #if !defined (TARGET_AARCH64_MS_ABI)
diff --git a/gcc/testsuite/gcc.target/i386/visibility-hidden-mingw-32.c b/gcc/testsuite/gcc.target/i386/visibility-hidden-mingw-32.c
new file mode 100644
index 000000000000..a952c71a013f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/visibility-hidden-mingw-32.c
@@ -0,0 +1,12 @@
+/* { dg-do compile { target i?86-*-mingw32* i?86-*-cygwin* } } */
+/* { dg-options "-fvisibility=hidden" } */
+
+void some_cdecl (int, int) {}
+void __attribute__((stdcall)) some_stdcall (int, int) {}
+void __attribute__((fastcall)) some_fastcall (int, int) {}
+
+/* Hidden visibility on 32-bit PE/COFF drops the user label prefix in
+   .drectve, but keeps stdcall and fastcall decoration.  */
+/* { dg-final { scan-assembler {-exclude-symbols:some_cdecl} } } */
+/* { dg-final { scan-assembler {-exclude-symbols:some_stdcall@8} } } */
+/* { dg-final { scan-assembler {-exclude-symbols:@some_fastcall@8} } } */
diff --git a/gcc/testsuite/gcc.target/i386/visibility-hidden-mingw.c b/gcc/testsuite/gcc.target/i386/visibility-hidden-mingw.c
new file mode 100644
index 000000000000..12d792631d4b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/visibility-hidden-mingw.c
@@ -0,0 +1,24 @@
+/* { dg-do link { target *-*-mingw* *-*-cygwin* } } */
+/* { dg-require-dll "" } */
+/* { dg-options "-shared -fvisibility=hidden -Wl,--output-def,visibility-hidden-mingw.def" } */
+
+void __attribute__((visibility("default"))) exported_func(void) {}
+void hidden_func(void) {}
+void __attribute__((visibility("hidden"))) explicit_hidden_func(void) {}
+void __attribute__((visibility("internal"))) internal_func(void) {}
+
+/* exported_func has default visibility, so it should be exported.  */
+/* { dg-final { scan-file visibility-hidden-mingw.def "(?n)^\\s*exported_func(?:\\s+@\[0-9\]+)?$" } } */
+
+/* hidden_func gets hidden from -fvisibility=hidden, so it should not be
+   auto-exported.  */
+/* { dg-final { scan-file-not visibility-hidden-mingw.def "(?n)^\\s*hidden_func(?:\\s+@\[0-9\]+)?$" } } */
+
+/* explicit_hidden_func is explicitly hidden, so it should not be
+   auto-exported.  */
+/* { dg-final { scan-file-not visibility-hidden-mingw.def "(?n)^\\s*explicit_hidden_func(?:\\s+@\[0-9\]+)?$" } } */
+
+/* internal_func has internal visibility, so it should not be auto-exported.  */
+/* { dg-final { scan-file-not visibility-hidden-mingw.def "(?n)^\\s*internal_func(?:\\s+@\[0-9\]+)?$" } } */
+
+/* { dg-final { remove-build-file "visibility-hidden-mingw.def" } } */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.