[PATCH v2] gas: support for .pushsection and .popsection pseudo ops for coff

Johannes Khoshnazar-Thoma <[email protected]> Fri, 24 Jul 2026 14:28:51 +0200
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
Current Linux kernels make heavy use of the .pushsection and
.popsection pseudo ops. Normally, the Linux kernel is built
as an ELF object, where the assembler supports those pseudo
ops. When compiling the Linux kernel as a Windows/ReactOS
driver however, it must be compiled as a COFF object, since
the Windows/ReactOS kernel does not know how to load ELF
binaries.

In order to be able to compile the Linux kernel as a COFF
object (and further on as a PE32 native executable) the
implementation of those pseudo ops has been copiied from
the obj-elf.c support code to the obj-coff.c support code.
It has been verified that the Linux kernel works without
assembler errors when using an assembler with this patch.
Also, a mini-test for pushsection/popsection has been added
and checked if it succeeds (at least on the x86-64 Linux
architecture).

Signed-off-by: Johannes Khoshnazar-Thoma <[email protected]>
---
 gas/config/obj-coff.c            | 53 ++++++++++++++++++++++++++++++--
 gas/testsuite/gas/coff/coff.exp  |  2 ++
 gas/testsuite/gas/coff/pushpop.d | 10 ++++++
 gas/testsuite/gas/coff/pushpop.s |  6 ++++
 4 files changed, 69 insertions(+), 2 deletions(-)
 create mode 100644 gas/testsuite/gas/coff/pushpop.d
 create mode 100644 gas/testsuite/gas/coff/pushpop.s

diff --git a/gas/config/obj-coff.c b/gas/config/obj-coff.c
index 7732c0af911..44b0ad418d6 100644
--- a/gas/config/obj-coff.c
+++ b/gas/config/obj-coff.c
@@ -1536,6 +1536,31 @@ obj_coff_finalize_section_relocs (asection *sec, arelent **relocs,
   return true;
 }
 
+struct section_stack
+{
+  struct section_stack *next;
+  segT seg;
+  subsegT subseg;
+};
+
+static struct section_stack *section_stack;
+
+static void
+obj_coff_popsection (int ignore ATTRIBUTE_UNUSED)
+{
+  struct section_stack *top = section_stack;
+
+  if (top == NULL)
+    {
+      as_warn (_(".popsection without corresponding .pushsection; ignored"));
+      return;
+    }
+
+  section_stack = top->next;
+  subseg_set (top->seg, top->subseg);
+  free (top);
+}
+
 /* Implement the .section pseudo op:
   	.section name {, "flags"}
                   ^         ^
@@ -1559,7 +1584,7 @@ obj_coff_finalize_section_relocs (asection *sec, arelent **relocs,
    .section directive to be parsed in both ELF and COFF formats.  */
 
 void
-obj_coff_section (int ignore ATTRIBUTE_UNUSED)
+obj_coff_section (int push)
 {
   /* Strip out the section name.  */
   char *section_name;
@@ -1691,6 +1716,15 @@ obj_coff_section (int ignore ATTRIBUTE_UNUSED)
 	}
     }
 
+  if (push)
+    {
+      struct section_stack *elt = XNEW (struct section_stack);
+      elt->next = section_stack;
+      elt->seg = now_seg;
+      elt->subseg = now_subseg;
+      section_stack = elt;
+    }
+
   sec = subseg_new (name, exp);
 
   if (is_bss)
@@ -1898,6 +1932,8 @@ static const pseudo_typeS coff_pseudo_table[] =
   {"ident", obj_coff_ident, 0},
   {"line", obj_coff_line, 0},
   {"ln", obj_coff_ln, 0},
+  {"pushsection", obj_coff_section, 1},
+  {"popsection", obj_coff_popsection, 0},
   {"scl", obj_coff_scl, 0},
   {"sect", obj_coff_section, 0},
   {"sect.s", obj_coff_section, 0},
@@ -1937,13 +1973,26 @@ coff_separate_stab_sections (void)
   return 1;
 }
 
+void
+coff_end (void)
+{
+  if (!ENABLE_LEAK_CHECK)
+    return;
+  while (section_stack)
+    {
+      struct section_stack *top = section_stack;
+      section_stack = top->next;
+      free (top);
+    }
+}
+
 const struct format_ops coff_format_ops =
 {
   bfd_target_coff_flavour,
   0,	/* dfl_leading_underscore */
   1,	/* emit_section_symbols */
   0,    /* begin */
-  0,	/* end.  */
+  coff_end,	/* end.  */
   c_dot_file_symbol,
   coff_assign_symbol,
   coff_frob_symbol,
diff --git a/gas/testsuite/gas/coff/coff.exp b/gas/testsuite/gas/coff/coff.exp
index 3732a226e3b..d079a2c5800 100644
--- a/gas/testsuite/gas/coff/coff.exp
+++ b/gas/testsuite/gas/coff/coff.exp
@@ -38,3 +38,5 @@ if { ![istarget *c4x*-*-*] && ![istarget *c54x*-*-*] } {
     run_dump_test func3
     run_dump_test func4
 }
+
+run_dump_test pushpop
diff --git a/gas/testsuite/gas/coff/pushpop.d b/gas/testsuite/gas/coff/pushpop.d
new file mode 100644
index 00000000000..33af1b01694
--- /dev/null
+++ b/gas/testsuite/gas/coff/pushpop.d
@@ -0,0 +1,10 @@
+#objdump: -s
+#name: .pushsection and .popsection support
+
+.*:     file format .*
+
+Contents of section a:
+.*0000.*01030000.*
+Contents of section b:
+.*0000.*02000000.*
+#pass
diff --git a/gas/testsuite/gas/coff/pushpop.s b/gas/testsuite/gas/coff/pushpop.s
new file mode 100644
index 00000000000..237c0a0d9de
--- /dev/null
+++ b/gas/testsuite/gas/coff/pushpop.s
@@ -0,0 +1,6 @@
+.section a
+.byte 1
+.pushsection b
+.byte 2
+.popsection
+.byte 3
-- 
2.55.0