[binutils-gdb] gas/coff: support for .previous and .{push, pop}section pseudo ops

Jan Beulich jbeulich@sourceware.org
Fri Sep 11 11:48:24 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=636a636c473a55d3420ac4bffd1dd4c223b96408

commit 636a636c473a55d3420ac4bffd1dd4c223b96408
Author: Johannes Khoshnazar-Thoma <johannes@johannesthoma.com>
Date:   Fri Sep 11 13:44:56 2026 +0200

    gas/coff: support for .previous and .{push,pop}section pseudo ops
    
    Add support for the .previous, .pushsection and .popsection pseudo-ops for
    COFF targets. The semantics are the same as their ELF counterparts.
    
    This patch is one building block for compiling the Linux kernel
    as a Windows/ReactOS driver.
    
    Signed-off-by: Johannes Khoshnazar-Thoma <johannes@johannesthoma.com>

Diff:
---
 gas/NEWS                               |   4 ++
 gas/config/obj-coff.c                  | 104 ++++++++++++++++++++++++++++++++-
 gas/config/obj-coff.h                  |   1 +
 gas/config/tc-ppc.c                    |   4 ++
 gas/config/tc-tic4x.c                  |   3 +
 gas/doc/as.texi                        |  11 +++-
 gas/read.c                             |   7 ++-
 gas/testsuite/gas/coff/coff.exp        |   3 +
 gas/testsuite/gas/coff/previous.d      |   9 +++
 gas/testsuite/gas/coff/previous.s      |   8 +++
 gas/testsuite/gas/coff/pushpop.d       |   9 +++
 gas/testsuite/gas/coff/pushpop.s       |   6 ++
 gas/testsuite/gas/ppc/aix.exp          |   2 +
 gas/testsuite/gas/ppc/xcoff-previous.d |   9 +++
 gas/testsuite/gas/ppc/xcoff-previous.s |  10 ++++
 15 files changed, 182 insertions(+), 8 deletions(-)

diff --git a/gas/NEWS b/gas/NEWS
index 8b07d86daa3..64674e6e490 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,5 +1,9 @@
 -*- text -*-
 
+* Support for .previous, .pushsection and .popsection for COFF targets.
+  The semantics are the same as for ELF targets. This allows one to
+  compile the Linux kernel as a Windows/ReactOS driver.
+
 * Add support for the x86 AVX10_V2_AUX instructions.
 
 * The avr target supports .gnu_attribute 4 (Tag_GNU_AVR_VTABLE_AS).
diff --git a/gas/config/obj-coff.c b/gas/config/obj-coff.c
index b08295019d8..e9cbe16029a 100644
--- a/gas/config/obj-coff.c
+++ b/gas/config/obj-coff.c
@@ -164,7 +164,10 @@ static void
 obj_coff_bss (int ignore ATTRIBUTE_UNUSED)
 {
   if (*input_line_pointer == '\n')
-    subseg_new (".bss", get_absolute_expression ());
+    {
+      obj_coff_section_change_hook ();
+      subseg_new (".bss", get_absolute_expression ());
+    }
   else
     s_lcomm (0);
 }
@@ -1540,6 +1543,73 @@ obj_coff_finalize_section_relocs (asection *sec, arelent **relocs,
   return true;
 }
 
+static segT previous_section;
+static subsegT previous_subsection;
+
+/* This can be called from the processor backends if they change
+   sections.  */
+
+void
+obj_coff_section_change_hook (void)
+{
+  previous_section = now_seg;
+  previous_subsection = now_subseg;
+}
+
+static void
+obj_coff_previous (int ignore ATTRIBUTE_UNUSED)
+{
+  segT new_section;
+  subsegT new_subsection;
+
+  if (previous_section == NULL)
+    {
+      as_warn (_(".previous without corresponding .section; ignored"));
+      return;
+    }
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  new_section = previous_section;
+  new_subsection = previous_subsection;
+  obj_coff_section_change_hook ();
+
+  subseg_set (new_section, new_subsection);
+}
+
+struct section_stack
+{
+  struct section_stack *next;
+  segT seg, prev_seg;
+  subsegT subseg, prev_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;
+    }
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  section_stack = top->next;
+  previous_section = top->prev_seg;
+  previous_subsection = top->prev_subseg;
+  subseg_set (top->seg, top->subseg);
+  free (top);
+}
+
 /* Implement the .section pseudo op:
   	.section name {, "flags"}
                   ^         ^
@@ -1563,7 +1633,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;
@@ -1695,6 +1765,18 @@ 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->prev_seg = previous_section;
+      elt->subseg = now_subseg;
+      elt->prev_subseg = previous_subsection;
+      section_stack = elt;
+    }
+
+  obj_coff_section_change_hook ();
   sec = subseg_new (name, exp);
 
   if (is_bss)
@@ -1902,6 +1984,9 @@ static const pseudo_typeS coff_pseudo_table[] =
   {"ident", obj_coff_ident, 0},
   {"line", obj_coff_line, 0},
   {"ln", obj_coff_ln, 0},
+  {"popsection", obj_coff_popsection, 0},
+  {"previous", obj_coff_previous, 0},
+  {"pushsection", obj_coff_section, 1},
   {"scl", obj_coff_scl, 0},
   {"sect", obj_coff_section, 0},
   {"sect.s", obj_coff_section, 0},
@@ -1941,13 +2026,26 @@ coff_separate_stab_sections (void)
   return 1;
 }
 
+static 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,
   c_dot_file_symbol,
   coff_assign_symbol,
   coff_frob_symbol,
diff --git a/gas/config/obj-coff.h b/gas/config/obj-coff.h
index 0b933f467b2..7a01401ea0e 100644
--- a/gas/config/obj-coff.h
+++ b/gas/config/obj-coff.h
@@ -318,6 +318,7 @@ extern void coff_obj_read_begin_hook     (void);
 extern void pecoff_obj_set_weak_hook     (symbolS *);
 extern void pecoff_obj_clear_weak_hook   (symbolS *);
 #endif
+extern void obj_coff_section_change_hook (void);
 extern void obj_coff_section             (int);
 extern segT obj_coff_add_segment         (const char *);
 extern void obj_coff_def                 (int);
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
index b82ba2eaebd..c0382635cf4 100644
--- a/gas/config/tc-ppc.c
+++ b/gas/config/tc-ppc.c
@@ -4529,6 +4529,8 @@ ppc_csect (int ignore ATTRIBUTE_UNUSED)
 static void
 ppc_change_csect (symbolS *sym, offsetT align)
 {
+  obj_coff_section_change_hook ();
+
   if (S_IS_DEFINED (sym))
     subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg);
   else
@@ -4652,6 +4654,7 @@ ppc_change_debug_section (unsigned int idx, subsegT subseg)
   flagword oldflags;
   const struct xcoff_dwsect_name *dw = &xcoff_dwsect_names[idx];
 
+  obj_coff_section_change_hook ();
   sec = subseg_new (dw->xcoff_name, subseg);
   oldflags = bfd_section_flags (sec);
   if (oldflags == SEC_NO_FLAGS)
@@ -5650,6 +5653,7 @@ ppc_ec (int ignore ATTRIBUTE_UNUSED)
 static void
 ppc_toc (int ignore ATTRIBUTE_UNUSED)
 {
+  obj_coff_section_change_hook ();
   if (ppc_toc_csect != NULL)
     subseg_set (data_section, symbol_get_tc (ppc_toc_csect)->subseg);
   else
diff --git a/gas/config/tc-tic4x.c b/gas/config/tc-tic4x.c
index a6f8d7a203b..7a27c06dc93 100644
--- a/gas/config/tc-tic4x.c
+++ b/gas/config/tc-tic4x.c
@@ -988,6 +988,9 @@ tic4x_sect (int x ATTRIBUTE_UNUSED)
   else
     num = 0;
 
+#ifdef OBJ_COFF
+  obj_coff_section_change_hook ();
+#endif
   seg = subseg_new (name, num);
   if (line_label != NULL)
     {
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 2cf4bdfe610..070ead47246 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -6700,7 +6700,7 @@ undefined.
 This is one of the ELF section stack manipulation directives.  The others are
 @code{.section} (@pxref{Section}), @code{.subsection} (@pxref{SubSection}),
 @code{.pushsection} (@pxref{PushSection}), and @code{.previous}
-(@pxref{Previous}).
+(@pxref{Previous}). It is also implemented on COFF targets.
 
 This directive replaces the current section (and subsection) with the top
 section (and subsection) on the section stack.  This section is popped off the
@@ -6716,7 +6716,7 @@ stack.
 This is one of the ELF section stack manipulation directives.  The others are
 @code{.section} (@pxref{Section}), @code{.subsection} (@pxref{SubSection}),
 @code{.pushsection} (@pxref{PushSection}), and @code{.popsection}
-(@pxref{PopSection}).
+(@pxref{PopSection}). It is also implemented on COFF targets.
 
 This directive swaps the current section (and subsection) with most recently
 referenced section/subsection pair prior to this one.  Multiple
@@ -6820,7 +6820,7 @@ expanded.  @xref{Macro}.
 This is one of the ELF section stack manipulation directives.  The others are
 @code{.section} (@pxref{Section}), @code{.subsection} (@pxref{SubSection}),
 @code{.popsection} (@pxref{PopSection}), and @code{.previous}
-(@pxref{Previous}).
+(@pxref{Previous}). It is also implemented on COFF targets.
 
 This directive pushes the current section (and subsection) onto the
 top of the section stack, and then replaces the current section and
@@ -6996,6 +6996,11 @@ will be as if no flags had been specified at all.
 
 If the optional argument to the @code{.section} directive is not quoted, it is
 taken as a subsection number (@pxref{Sub-Sections}).
+
+For COFF targets, the @code{.pushsection} (@pxref{PushSection}),
+@code{.popsection} (@pxref{PopSection}), and @code{.previous}
+(@pxref{Previous}) directives are also implemented.
+
 @end ifset
 
 @ifset ELF
diff --git a/gas/read.c b/gas/read.c
index ae481007c32..99405ef6834 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -3947,11 +3947,14 @@ s_struct (int ignore ATTRIBUTE_UNUSED)
   if (flag_mri)
     stop = mri_comment_field (&stopc);
   abs_section_offset = get_absolute_expression ();
-#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
-  /* The ELF backend needs to know that we are changing sections, so
+  /* The ELF and COFF backends need to know that we are changing sections, so
      that .previous works correctly. */
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
   if (IS_ELF)
     obj_elf_section_change_hook ();
+#endif
+#if defined (OBJ_COFF)
+  obj_coff_section_change_hook ();
 #endif
   subseg_set (absolute_section, 0);
   demand_empty_rest_of_line ();
diff --git a/gas/testsuite/gas/coff/coff.exp b/gas/testsuite/gas/coff/coff.exp
index 3732a226e3b..323c59c21ce 100644
--- a/gas/testsuite/gas/coff/coff.exp
+++ b/gas/testsuite/gas/coff/coff.exp
@@ -38,3 +38,6 @@ if { ![istarget *c4x*-*-*] && ![istarget *c54x*-*-*] } {
     run_dump_test func3
     run_dump_test func4
 }
+
+run_dump_test pushpop
+run_dump_test previous
diff --git a/gas/testsuite/gas/coff/previous.d b/gas/testsuite/gas/coff/previous.d
new file mode 100644
index 00000000000..495fb245cab
--- /dev/null
+++ b/gas/testsuite/gas/coff/previous.d
@@ -0,0 +1,9 @@
+#objdump: -s
+#name: .previous support
+
+.*:     file format .*
+
+Contents of section a:
+ 0000.*01[0 ]*03.*
+Contents of section b:
+ 0000.*02[0 ]*04.*
diff --git a/gas/testsuite/gas/coff/previous.s b/gas/testsuite/gas/coff/previous.s
new file mode 100644
index 00000000000..c47ff8ab5af
--- /dev/null
+++ b/gas/testsuite/gas/coff/previous.s
@@ -0,0 +1,8 @@
+	.section a
+	.byte 1
+	.section b
+	.byte 2
+	.previous
+	.byte 3
+	.previous
+	.byte 4
diff --git a/gas/testsuite/gas/coff/pushpop.d b/gas/testsuite/gas/coff/pushpop.d
new file mode 100644
index 00000000000..7d3ce21a273
--- /dev/null
+++ b/gas/testsuite/gas/coff/pushpop.d
@@ -0,0 +1,9 @@
+#objdump: -s
+#name: .pushsection and .popsection support
+
+.*:     file format .*
+
+Contents of section a:
+ 0000.*01[0 ]*03.*
+Contents of section b:
+ 0000.*02.*
diff --git a/gas/testsuite/gas/coff/pushpop.s b/gas/testsuite/gas/coff/pushpop.s
new file mode 100644
index 00000000000..ec05bade0dc
--- /dev/null
+++ b/gas/testsuite/gas/coff/pushpop.s
@@ -0,0 +1,6 @@
+	.section a
+	.byte 1
+	.pushsection b
+	.byte 2
+	.popsection
+	.byte 3
diff --git a/gas/testsuite/gas/ppc/aix.exp b/gas/testsuite/gas/ppc/aix.exp
index 613c71b81ff..6f195289220 100644
--- a/gas/testsuite/gas/ppc/aix.exp
+++ b/gas/testsuite/gas/ppc/aix.exp
@@ -79,6 +79,8 @@ if { [istarget "powerpc*-*-aix*"] || [istarget "rs6000-*-aix*"] } then {
     run_dump_test "xcoff-function-1-32"
     run_dump_test "xcoff-function-1-64"
 
+    run_dump_test "xcoff-previous"
+
     run_dump_test "xcoff-tls-32"
     run_dump_test "xcoff-tls-64"
 
diff --git a/gas/testsuite/gas/ppc/xcoff-previous.d b/gas/testsuite/gas/ppc/xcoff-previous.d
new file mode 100644
index 00000000000..838475b0b8a
--- /dev/null
+++ b/gas/testsuite/gas/ppc/xcoff-previous.d
@@ -0,0 +1,9 @@
+#objdump: -s
+#name: .previous support for xcoff
+
+.*:     file format .*
+
+Contents of section .text:
+ 0000.*01[0 ]*03.*
+Contents of section .data:
+ 0008.*02[0 ]*04.*
diff --git a/gas/testsuite/gas/ppc/xcoff-previous.s b/gas/testsuite/gas/ppc/xcoff-previous.s
new file mode 100644
index 00000000000..0a5d86b0063
--- /dev/null
+++ b/gas/testsuite/gas/ppc/xcoff-previous.s
@@ -0,0 +1,10 @@
+	.globl .a
+	.globl .b
+	.section .text
+	.byte 1
+	.section .data
+	.byte 2
+	.previous
+	.byte 3
+	.previous
+	.byte 4


More information about the Binutils-cvs mailing list