[PATCH] Rust: backend: Add proper handling for #[used] attribute.

Arthur Cohen arthur.cohen@opensrcsec.com
Wed Sep 16 14:30:32 GMT 2026


Updated version with a better testcase that doesn't fail on platforms 
using an older linker.

Thanks and thanks to the rustc_codegen_gcc team for reviewing it and 
ACKing it :)

Best,

Arthur

---

 From 31b188a0396c25cfca9f152f3a58d8188ab4274e Mon Sep 17 00:00:00 2001
From: Arthur Cohen <arthur.cohen@opensrcsec.com>
Date: Wed, 16 Sep 2026 05:02:30 +0200
Subject: [PATCH] backend: Add proper handling for #[used] attribute.

We now support the full implementation of the #[used] attribute, which
can be used to conserve a static variable even if the compiler deems it
unused. This commit also implements #[used(linker)], which instructs the
linker to not garbage collect the static even if it seems unused.

This behavior is equivalent to the `used` and `retain` attributes in C.

gcc/rust/ChangeLog:

	* backend/rust-compile-item.cc (handle_used_attr): New function.
	(CompileItem::visit): Call it.

gcc/testsuite/ChangeLog:

	* rust/compile/used-attr4.rs: New test.
---
  gcc/rust/backend/rust-compile-item.cc    | 61 ++++++++++++++++++------
  gcc/testsuite/rust/compile/used-attr4.rs | 16 +++++++
  2 files changed, 63 insertions(+), 14 deletions(-)
  create mode 100644 gcc/testsuite/rust/compile/used-attr4.rs

diff --git a/gcc/rust/backend/rust-compile-item.cc 
b/gcc/rust/backend/rust-compile-item.cc
index fa1e8eec99f..d9afdcacc3c 100644
--- a/gcc/rust/backend/rust-compile-item.cc
+++ b/gcc/rust/backend/rust-compile-item.cc
@@ -23,10 +23,53 @@
  #include "rust-substitution-mapper.h"
  #include "rust-type-util.h"
  #include "rust-finalized-name-resolution-context.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "target.h"

  namespace Rust {
  namespace Compile {

+static void
+handle_used_attr (HIR::StaticItem &var, tree decl)
+{
+  auto used_compiler = false; /* equivalent to C's used attribute */
+  auto used_linker = false;   /* equivalent to C's retain attribute */
+
+  // If the attribute has no argument, then it is equivalent to 
`used(compiler)`
+  for (const auto &attr : var.get_outer_attrs ())
+    {
+      if (attr.as_string () == "used")
+	used_compiler = true;
+
+      // We've already checked that the attribute was used properly, so 
just
+      // fetch the information we need
+      if (attr.has_attr_input ())
+	{
+	  auto input
+	    = static_cast<const AST::DelimTokenTree &> (attr.get_attr_input ())
+		.parse_to_meta_item ();
+
+	  for (const auto &item : input->get_items ())
+	    if (item->as_string () == "linker")
+	      used_linker = true;
+	}
+    }
+
+  if (used_compiler)
+    {
+      DECL_PRESERVE_P (decl) = 1;
+      DECL_READ_P (decl) = 1;
+    }
+
+  // Check that the linker supports the attribute, and then add it
+  if (SUPPORTS_SHF_GNU_RETAIN && used_linker)
+    {
+      DECL_ATTRIBUTES (decl)
+	= tree_cons (get_identifier ("retain"), NULL, DECL_ATTRIBUTES (decl));
+    }
+}
+
  void
  CompileItem::visit (HIR::StaticItem &var)
  {
@@ -71,24 +114,14 @@ CompileItem::visit (HIR::StaticItem &var)
    bool is_hidden = false;
    bool in_unique_section = true;

-  // NOTE: We currently compile global variables and always mark them 
as "used"
-  // with TREE_USED(tree). I'm not sure this is correct - they should 
be marked
-  // as used only if the `MarkLive` pass reaches them and flags them. 
However,
-  // the `#[used]` built-in attribute can be used here to *force* a static
-  // variable to be used by the compiler so that it isn't deleted.
-  // We should probably be doing something along the lines of:
-  //
-  // - Removing the `TREE_USED (decl) = 1` line in 
`Backend::global_variable`
-  // - Check if the static item has a `used` attribute
-  // - If it does, check whether it's the default `used` version, or 
the version
-  // with an argument - `used(compiler)` or `used(linker)`
-  // - If we have the attribute or the `compiler` version of the 
attribute, then
-  // we `TREE_USED` the decl.
-
    Bvariable *static_global
      = Backend::global_variable (name, asm_name, type, is_external, 
is_hidden,
  				in_unique_section, var.get_locus ());

+  // Static items are concerned by the #[used] attribute which needs to be
+  // handled here
+  handle_used_attr (var, static_global->get_decl ());
+
    tree init = value == error_mark_node ? error_mark_node : 
DECL_INITIAL (value);
    Backend::global_variable_set_init (static_global, init);

diff --git a/gcc/testsuite/rust/compile/used-attr4.rs 
b/gcc/testsuite/rust/compile/used-attr4.rs
new file mode 100644
index 00000000000..18078a8ca1f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/used-attr4.rs
@@ -0,0 +1,16 @@
+#![feature(no_core)]
+#![feature(used_with_arg)]
+#![no_core]
+
+#[used]
+static FOO0: i32 = 15;
+// { dg-final { scan-assembler "FOO0" } }
+
+#[used(compiler)]
+static FOO1: i32 = 15;
+// { dg-final { scan-assembler "FOO1" } }
+
+#[used(linker)]
+static FOO2: i32 = 15;
+// { dg-final { scan-assembler "FOO2" } }
+// { dg-final { scan-assembler ".section.*awR" { target gnu_retain } } }
-- 
2.50.1




More information about the Gcc-rust mailing list