Skip to content

post-process-pe: show section names in warnings/errors and strip COFF symbol tables - #815

Open
vathpela wants to merge 4 commits into
rhboot:mainfrom
vathpela:strip-coff-symtabs
Open

post-process-pe: show section names in warnings/errors and strip COFF symbol tables#815
vathpela wants to merge 4 commits into
rhboot:mainfrom
vathpela:strip-coff-symtabs

Conversation

@vathpela

@vathpela vathpela commented Jul 17, 2026

Copy link
Copy Markdown
Member

This PR addresses two issues in post-process-pe:

  1. when our sections have a problem we just report an ambiguous section number, not the name,
  2. Our current PE binaries include a COFF symbol table and string table that are being used for absolutely nothing except showing section names. On shimx64.efi that's about 400KiB and we're using ~50 bytes of it, and then only in e.g. objdump -h and (now) post-process-pe output.

This PR adds code to show the section names, and also an option to strip the COFF symbol table and re-write the COFF string table to only include our section names.

@vathpela
vathpela force-pushed the strip-coff-symtabs branch 9 times, most recently from 53e16e4 to 3769604 Compare July 22, 2026 17:24
@vathpela
vathpela marked this pull request as ready for review July 22, 2026 17:46
@vathpela
vathpela force-pushed the strip-coff-symtabs branch 2 times, most recently from fc920b9 to 79d7974 Compare July 23, 2026 17:40
vathpela added 4 commits July 27, 2026 16:45
Currently we have this in our mkosi configs:

  [Config]
  MinimumVersion=commit:dec7c3e754810ae6d2382d0b5e4762d7250b0254

The theory is this ensures we have a new enough mkosi for all the
features / configs we're using to actually work.  In practice this is
always true, so what this config option does is break things when I try
to duplicate results on a distro-packaged mkosi (i.e. not a git checkout
done by the github systemd/mkosi@main action), with this not at all
cryptic error message:

  $ mkosi sandbox -- mkosi --distribution fedora --kernel-command-line-extra=systemd.unit=mkosi-test.service qemu
  ‣ Cannot check mkosi git version, not running mkosi from a git repository

So this patch comments it out.  I'd remove it entirely, but it's useful
to see which version things were intended for.

Signed-off-by: Peter Jones <pjones@redhat.com>
Previously post-process-pe would complain like:

  validate_nx_compat():467: Section 9 has Virtual Address 0x00227000 that isn't section aligned (0x00010000)

which is moderately useful, but stealthily tricks the user into trying
to figure out if we're counting from 0 or 1, which then makes them have
to resolve this by looking at the actual addresses we're complaining
about to match it up.

This patch changes that output to:

  validate_nx_compat():467: Section 9 (".rodata") has Virtual Address 0x00227000 that isn't section aligned (0x00010000)

Signed-off-by: Peter Jones <pjones@redhat.com>
While investigating how to make post-process-pe show section names, I
discovered yet another way our objcopy magic binaries are amazing.

If you dump, say, our shimx64.efi binary with objdump, you'll see
something like:

  $ objdump -h shimx64.efi

  shimx64.efi:     file format pei-x86-64

  Sections:
  Idx Name          Size      VMA               LMA               File off  Algn
    0 .eh_frame     00038f6c  000000000000a000  000000000000a000  00001000  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
    1 .text         000e4d52  0000000000043000  0000000000043000  0003a000  2**4
                    CONTENTS, ALLOC, LOAD, READONLY, CODE
    2 .data.ident   00000066  0000000000129000  0000000000129000  0011f000  2**4
                    CONTENTS, ALLOC, LOAD, DATA
    3 .sbatlevel    0000004f  000000000012a000  000000000012a000  00120000  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
    4 .data         0005d128  000000000012b000  000000000012b000  00121000  2**4
                    CONTENTS, ALLOC, LOAD, DATA
    5 .reloc        0000000c  0000000000189000  0000000000189000  0017f000  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
    6 .vendor_cert  000008fa  000000000018a000  000000000018a000  00180000  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
    7 .dynamic      00000130  000000000018b000  000000000018b000  00181000  2**2
                    CONTENTS, ALLOC, LOAD, DATA
    8 .rela         00034de8  000000000018c000  000000000018c000  00182000  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
    9 .sbat         00000083  00000000001c1000  00000000001c1000  001b7000  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
   10 .dynsym       0001cd70  00000000001c2000  00000000001c2000  001b8000  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA
   11 .dynstr       0001a063  00000000001df000  00000000001df000  001d5000  2**2
                    CONTENTS, ALLOC, LOAD, READONLY, DATA

There's no .strtab or .symtab (and objdump would hide them from you
anyway...), because we post-processed this from a shared object, so
everything is in .dynsym and .dynstr.  So you figure the section names
are in there, right?  And this binary is approximately 2027619 bytes
long, plus maybe a few KiB for signatures at the end.  Right?

But then you look harder at ".eh_frame", the first section name longer
than 8 characters, has this in the raw section header:

  00000188                           2f 34 00 00 00 00 00 00          |/4......|
  00000190  6c 8f 03 00 00 a0 00 00  00 90 03 00 00 10 00 00  |l...............|
  000001a0  00 00 00 00 00 00 00 00  00 00 00 00 40 00 00 40  |............@..@|

In PE, the /4 means it's at position 4 in the strings.  But it's not:

  String section [15] '.dynstr' contains 106595 bytes at offset 0x1df000:
    [     0]
    [     1]  ImageBase
    [     b]  _relocate
    [    15]  __stack_chk_guard
    [    27]  SHIM_LOCK_GUID
    [    36]  get_variable
    [    43]  console_print
    [    51]  __stack_chk_fail

Because it's in another string table that's completely hidden!  Which
objdump knows how to look up but doesn't tell you about because it's not
in a section for no good reason.

If you dump the COFF file header, you'll see:

  0x8C       0x8   PointerToSymbolTable:          0x1F0000
  0x90       0xC   NumberOfSymbols:               0x3B1B

And sure enough if you hex dump at 0x1f0000 you'll see a COFF symbol
table that absolutely nothing is using, and if you dump
0x1f0000 + 0x3b1b * 18, you'll see:

  002327e6                    ff 4f  02 00 2e 65 68 5f 66 72       |..O...eh_fr|
  002327f0  61 6d 65 00 2e 64 61 74  61 2e 69 64 65 6e 74 00  |ame..data.ident.|
  00232800  2e 73 62 61 74 6c 65 76  65 6c 00 2e 76 65 6e 64  |.sbatlevel..vend|
  00232810  6f 72 5f 63 65 72 74 00  64 65 62 75 67 5f 68 6f  |or_cert.debug_ho|
  00232820  6f 6b 00 77 61 69 74 5f  66 6f 72 5f 64 65 62 75  |ok.wait_for_debu|

Which is a uint32_t that's 0x024fff and then a perfectly normal string
table that starts with, that's right, .eh_frame.  And if you add that
length up you get 0x2577e5.  And sure enough:

  002577c0  61 72 69 61 62 6c 65 5f  64 61 74 61 00 6f 73 73  |ariable_data.oss|
  002577d0  6c 5f 64 65 72 5f 6f 69  64 5f 63 32 74 6e 62 31  |l_der_oid_c2tnb1|
  002577e0  39 31 76 31 00                                    |91v1.|
  002577e5

That's the end of the binary.

tl;dr: there's 394KiB hanging around at the end of this binary, and the
only thing that's using it is using it is objdump, which is using... 49
bytes of it to show us section names.

This patch strips the COFF symbol table entirely, and replaces the COFF
string table with one that includes only the section names.

Signed-off-by: Peter Jones <pjones@redhat.com>
This moves all of the flags for post-process-pe to POST_PROCESS_PE_FLAGS
and sets a default of "-c -vv" in Make.defaults.

Signed-off-by: Peter Jones <pjones@redhat.com>
@vathpela
vathpela force-pushed the strip-coff-symtabs branch from 79d7974 to a52a4db Compare July 27, 2026 20:46
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 58.77%. Comparing base (72fd4b7) to head (a52a4db).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #815      +/-   ##
==========================================
- Coverage   59.48%   58.77%   -0.71%     
==========================================
  Files          24       24              
  Lines        4226     4226              
  Branches      655      655              
==========================================
- Hits         2514     2484      -30     
  Misses       1471     1471              
- Partials      241      271      +30     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant