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

Message ID 20260724122850.469382-2-johannes@johannesthoma.com
State New
Headers
Series [v2] gas: support for .pushsection and .popsection pseudo ops for coff |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Johannes Khoshnazar-Thoma July 24, 2026, 12:28 p.m. UTC
  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 <johannes@johannesthoma.com>
---
 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
  

Comments

Maciej W. Rozycki July 26, 2026, 5:06 a.m. UTC | #1
Hi Johannes,

 Thank you for your submission.  I've left the GAS code pieces for Jan to 
sort out, since he's picked up this part already.  Here are a couple of my 
observations as to the remaining parts of the change.

 Please reword using the imperative mood both for the change heading and 
the description.

> 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

s/copiied/copied/

> Also, a mini-test for pushsection/popsection has been added
> and checked if it succeeds (at least on the x86-64 Linux
> architecture).

 We don't record in the change description what regression-testing has 
been done; that is good to mention in the change discussion (or cover 
letter if applicable).

 I've regression-tested your change across 263 targets, several of which 
using COFF.  There are a couple of issues:

tic4x-coff  +FAIL: .pushsection and .popsection support
tic54x-coff  +FAIL: .pushsection and .popsection support
z80-coff  +FAIL: .pushsection and .popsection support
z8k-coff  +FAIL: .pushsection and .popsection support

Specifically (first error quoted only):

.../gas/testsuite/gas/coff/pushpop.s:1: Error: Unknown opcode `a'.
.../gas/testsuite/gas/coff/pushpop.s:1: Error: Invalid label '.section'

for the first two respectively, and:

regexp_diff match failure
regexp "^.*0000.*01030000.*$"
line   " 0000 0103                                 ..              "
regexp_diff match failure
regexp "^.*0000.*02000000.*$"
line   " 0000 02                                   .               "

for the latter two.

 Evidently the TIC4X/TIC54X dialects are pickier about code formatting, so 
please add the missing leading tabs.  Additionally these targets have 
multi-octet bytes, so even with formatting fixed the output does not match 
the dump patterns.  I think the patterns will best be made stricter on the 
offset and more relaxed on the data, e.g.:

Contents of section a:
 0000 01[0 ]*03.*
Contents of section b:
 0000 02.*

-- because all we want to verify is that the data items are correctly 
assigned to the respective sections.  The #pass statement does not seem to 
be needed or wanted, as there's no trailing output expected.

 I second Jan in that we want a complete feature rather than adding it 
piecemeal, especially given that it's a small piece of code, so please 
implement `.previous' as well (which is also historically older and goes 
back almost to the beginning of our ELF support), so that e.g. autoconf 
feature checks won't have to be added for individual pseudo-ops (and in 
principle given the timeline an existing check out there may actually 
infer `.previous' support from `.pushsection'/`.popsection' presence).

 Last but not least please update the manual accordingly and propose a 
gas/NEWS entry, since it's a new feature.

  Maciej
  
Jan Beulich July 31, 2026, 9:58 a.m. UTC | #2
On 24.07.2026 14:28, Johannes Khoshnazar-Thoma wrote:
> @@ -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},

While now you insert at the right spot, what you insert is still the wrong
way round. Beyond this things look okay now as far as these two directives
go, but (to re-state the obvious) support for .previous is still missing.

Jan
  
Johannes Khoshnazar-Thoma July 31, 2026, 11:22 a.m. UTC | #3
Am 31.07.26 um 11:58 schrieb Jan Beulich:
> On 24.07.2026 14:28, Johannes Khoshnazar-Thoma wrote:
>> @@ -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},
> 
> While now you insert at the right spot, what you insert is still the wrong
> way round. Beyond this things look okay now as far as these two directives
> go, but (to re-state the obvious) support for .previous is still missing.
> 
> Jan

Hi Jan, Maciej,

Thank you for looking at my patch. I will address your concerns,
but I have a question regarding .previous support: in the obj_elf.c
code there is a obj_elf_section_change_hook() function which is
also called at many places in the processor-specific backends
(and also from read.c). Implementing .previous would require
adding a obj_coff_section_change_hook() to be called when
anyone changes the section in other files, am I right? Or is
it not necessary in the coff case to have such a hook?

How would I identify backends that have coff support? And how
would I be able to test all those backends?

  - Johannes
  
Jan Beulich July 31, 2026, 11:49 a.m. UTC | #4
On 31.07.2026 13:22, Johannes Khoshnazar-Thoma wrote:
> Am 31.07.26 um 11:58 schrieb Jan Beulich:
>> On 24.07.2026 14:28, Johannes Khoshnazar-Thoma wrote:
>>> @@ -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},
>>
>> While now you insert at the right spot, what you insert is still the wrong
>> way round. Beyond this things look okay now as far as these two directives
>> go, but (to re-state the obvious) support for .previous is still missing.
> 
> Thank you for looking at my patch. I will address your concerns,
> but I have a question regarding .previous support: in the obj_elf.c
> code there is a obj_elf_section_change_hook() function which is
> also called at many places in the processor-specific backends
> (and also from read.c). Implementing .previous would require
> adding a obj_coff_section_change_hook() to be called when
> anyone changes the section in other files, am I right? Or is
> it not necessary in the coff case to have such a hook?

That's going to be necessary, yes.

> How would I identify backends that have coff support?

Look for uses of fmt=coff in gas/configure.tgt.

> And how would I be able to test all those backends?

By building cross tools (passing --target=... to configure) and then running
"make check" on them just like you do for natively built tools.

Jan
  

Patch

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