Browse Source

G-128: fleet renamed to PUNKSHELL via punkres; punkres 0.3.0 -drop-opaque-overlay for mingw stub symbol tables; punkshell 0.30.3

Forced fleet rebake (user-directed): all ten win32 kits now carry the embedded
PUNKSHELL group icon via punkres, verified per kit (punkres list, sidecar
icon_group=PUNKSHELL + embedding=punkres, extract-ico byte-identical spot
check) and booting (incl. metakit punksys 8.6.13 / punk86 8.6.17 and
punk902z 9.0.2). punkmagic excluded (runtime absent - keeps its old icon and
_build cache); punkshell902 linux = not applicable.

The first fleet pass surfaced a real shape: mingw-built runtimes (tclsh902z,
tclkit-win64-dyn family) keep COFF symbol/debug tables AFTER their sections,
the extraction head carries them, and punkres correctly refused those stubs
as data-classified overlay. Measured against twapi-era artifacts: Windows'
EndUpdateResource has always silently STRIPPED exactly those bytes at
stub-stamp time (leaving PointerToSymbolTable dangling) and the kits work -
so punkres 0.3.0 (upstream dca8634, re-vendored, 33/33) encodes the
distinction: -drop-opaque-overlay DISCARDS expendable trailing data and
zeroes the orphaned symbol-table header fields, vs -allow-opaque-overlay
which RELOCATES a real self-locating payload; conflicting flags rejected;
drop is inert on zip overlays. The seam's punkres arm passes -drop
(twapi-arm-equivalent stub hygiene; bin/AGENTS.md documents it).

Recovery note: a 10-minute foreground timeout killed the first 3-kit rerun
during deploy - stale src/_build/.punkcheck.lock removed after a dead-pid
check per the recorded procedure; the completion bake deployed the
already-wrapped kits. Full punkexe suite 83 tests 0 failed; goals_lint +
architecture_lint clean.

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 4 days ago
parent
commit
c6d50b3b2a
  1. 15
      CHANGELOG.md
  2. 12
      bin/AGENTS.md
  3. 36
      goals/G-128-portable-pe-resource-stamping.md
  4. 2
      punkproject.toml
  5. 9
      src/make.tcl
  6. 4
      src/tools/punkres/PROVENANCE.md
  7. 12
      src/tools/punkres/README.md
  8. 2
      src/tools/punkres/build.zig.zon
  9. 59
      src/tools/punkres/src/punkres.zig
  10. 82
      src/tools/punkres/src/punkres_test.zig

15
CHANGELOG.md

@ -5,6 +5,21 @@ The latest `## [X.Y.Z]` header must match the `version` field in `punkproject.to
Entries are newest-first; one bullet per notable change. See the root `AGENTS.md` Entries are newest-first; one bullet per notable change. See the root `AGENTS.md`
"Project Versioning" section for the bump policy. "Project Versioning" section for the bump policy.
## [0.30.3] - 2026-07-29
- Fleet renamed: every win32 kit now carries the embedded `PUNKSHELL` group
icon via punkres (punksys, punk86, punk91, punkbi, punkluck86, punk902z,
punk905, punk91ix86, punk9_beta, punk9bi_beta; punkmagic excluded - its
runtime is absent; punkshell902 is a linux target, not applicable).
- punkres 0.3.0: new `set-icon -drop-opaque-overlay` consent DISCARDS non-zip
trailing stub bytes (the COFF symbol/debug tables mingw-built runtimes
carry, which ride into extraction heads and previously made punkres refuse
those stubs), zeroing the orphaned symbol-table header pointer; distinct
from `-allow-opaque-overlay` which RELOCATES a real self-locating payload.
The bake's punkres arm passes the drop flag - matching the twapi arm's
measured long-standing EndUpdateResource stripping - so mingw-lineage stubs
(tclsh902z, tclkit-win64-dyn family) stamp identically to MSVC/zig ones.
## [0.30.2] - 2026-07-29 ## [0.30.2] - 2026-07-29
- Baked kits' embedded RT_GROUP_ICON is now NAMED from the icon file's - Baked kits' embedded RT_GROUP_ICON is now NAMED from the icon file's

12
bin/AGENTS.md

@ -171,9 +171,15 @@ is selected when present; otherwise the twapi arm serves windows hosts with
nothing built. The embedded RT_GROUP_ICON is NAMED from the icon file's nothing built. The embedded RT_GROUP_ICON is NAMED from the icon file's
uppercased rootname (`punkshell.ico` -> `PUNKSHELL`, RC convention; applies uppercased rootname (`punkshell.ico` -> `PUNKSHELL`, RC convention; applies
to per-kit overrides identically since they are also named `punkshell.ico`), to per-kit overrides identically since they are also named `punkshell.ico`),
with the resource language adopted from the group being replaced. Named with the resource language adopted from the group being replaced. Stub
"resources", not "icon", so the parked RT_VERSION stamping extends the same hygiene: non-zip trailing bytes on a stub (the COFF symbol/debug tables
file. mingw-built runtimes carry after their sections, which ride into the
extraction head) are DROPPED at stamp time - the punkres arm via its
`-drop-opaque-overlay` consent (which also zeroes the orphaned
symbol-table header pointer), the twapi arm implicitly via
`EndUpdateResource`'s long-standing stripping - so both arms emit the same
payload-free stamped stub. Named "resources", not "icon", so the parked
RT_VERSION stamping extends the same file.
- Icon choice: a root-level `punkshell.ico` in the kit's own custom `.vfs` - Icon choice: a root-level `punkshell.ico` in the kit's own custom `.vfs`
folder (consulted pre-merge, so the `_vfscommon.vfs` copy never reads as an folder (consulted pre-merge, so the `_vfscommon.vfs` copy never reads as an

36
goals/G-128-portable-pe-resource-stamping.md

@ -166,6 +166,26 @@ the parked RT_VERSION stamping wants, since the project version is known late.
start and moves by exactly the shift delta. punkres handles this (verify start and moves by exactly the shift delta. punkres handles this (verify
invariant base2 == base1 + delta) and reports the leading-byte distance via invariant base2 == base1 + delta) and reports the leading-byte distance via
an overlay_note line. an overlay_note line.
- COFF symbol-table finding (2026-07-29, fleet rebake): mingw-built runtimes
(tclsh902z, the tclkit-win64-dyn* family) keep their COFF symbol/debug
tables AFTER the sections (located by the COFF header's
PointerToSymbolTable, outside every section - .file/crtbegin.c strings),
and the extraction head carries them, so the "payload-free" stub handed to
the icon seam has a data-classified overlay (306KB on tclsh902z, ~272KB on
the tclkits). punkres correctly REFUSED those stubs where MSVC/zig-built
stubs (symbols stripped to PDB / none) stamped clean. Measured against the
twapi-era artifacts: EndUpdateResource has ALWAYS silently stripped exactly
those bytes at stub-stamp time - the working twapi-era punk902z has zip
base == overlay start with the 306KB gone, the twapi-era punksys has the
metakit starting immediately at PE end, and both left PointerToSymbolTable
DANGLING (2294272 with the table gone) - so the bytes are proven
expendable for assembled kits. punkres 0.3.0 encodes the distinction as
two consent verbs: -allow-opaque-overlay RELOCATES (self-locating payloads
like metakit), -drop-opaque-overlay DISCARDS (expendable trailing data),
zeroing the symbol-table header fields it orphans rather than leaving them
dangling; the seam's punkres arm passes -drop (stub context: trailing
non-zip bytes are detritus by contract, matching the twapi arm's proven
de-facto behaviour), and the flags conflict if combined.
- Recorded 2026-07-29 (user decision, increment-1 review): the acceptance's - Recorded 2026-07-29 (user decision, increment-1 review): the acceptance's
'kit' (sdx/metakit) shape clause is SATISFIED FOR NOW by the measured 'kit' (sdx/metakit) shape clause is SATISFIED FOR NOW by the measured
consent path (-allow-opaque-overlay; moved payload boots - see Progress and consent path (-allow-opaque-overlay; moved payload boots - see Progress and
@ -301,3 +321,19 @@ the parked RT_VERSION stamping wants, since the project version is known late.
(codepage divergence as per the Notes finding); sidecar icon_group + (codepage divergence as per the Notes finding); sidecar icon_group +
embedding recorded; final deployed state punkres-stamped and booting. embedding recorded; final deployed state punkres-stamped and booting.
punkshell 0.30.2. punkshell 0.30.2.
- 2026-07-29 increment 1c (user-directed fleet rebake): ALL ten win32 kits now
wear RT_GROUP_ICON "PUNKSHELL" lang 1033 embedded via punkres, verified by
punkres list on every deployed kit, sidecars icon_group=PUNKSHELL +
embedding=punkres, extract-ico byte-identical to punkshell.ico (spot
punk902z), and boots: punk91-family/punkluck86/punk91ix86 (earlier
increments) + punksys 8.6.13, punk86 8.6.17, punk902z 9.0.2 (this one).
punkshell902 (linux) NOT APPLICABLE as designed; punkmagic EXCLUDED (its
tclsh90magic runtime is absent - it keeps its old icon and its _build cache
until the runtime returns). The first fleet pass surfaced the COFF
symbol-table refusals on the three mingw-lineage stubs (see Notes) - fixed
properly via punkres 0.3.0 -drop-opaque-overlay (upstream dca8634,
re-vendored, suite 33/33) with the seam passing the flag; second pass
stamped all three (a foreground timeout killed the first 3-kit rerun during
deploy - stale .punkcheck.lock removed per the recorded recovery procedure,
completion bake deployed the already-wrapped kits). Full punkexe suite 83
tests 0 failed. punkshell 0.30.3.

2
punkproject.toml

@ -1,4 +1,4 @@
[project] [project]
name = "punkshell" name = "punkshell"
version = "0.30.2" version = "0.30.3"
license = "BSD-2-Clause" license = "BSD-2-Clause"

9
src/make.tcl

@ -6562,6 +6562,15 @@ proc ::punkboot::kit_icon_embed_punkres {punkres_exe exepath icopath {group_over
if {$group_override ne ""} { if {$group_override ne ""} {
lappend cmd -group $group_override lappend cmd -group $group_override
} }
#-drop-opaque-overlay: the stub contract is a payload-free prefix, so any
#non-zip trailing bytes are build detritus - concretely the COFF
#symbol/debug tables mingw-built runtimes carry after their sections,
#which ride into the extraction head. The twapi arm's EndUpdateResource
#has always silently stripped exactly those bytes (measured 2026-07-29,
#G-128 detail); punkres drops them deliberately and zeroes the dangling
#symbol-table header pointer. A REAL zip payload on a stub is unaffected
#(the flag is inert for zip-classified overlays, which punkres preserves).
lappend cmd -drop-opaque-overlay
lappend cmd [file nativename $exepath] [file nativename $icopath] 2>@1 lappend cmd [file nativename $exepath] [file nativename $icopath] 2>@1
if {[catch $cmd output]} { if {[catch $cmd output]} {
error "punkres set-icon failed: $output" error "punkres set-icon failed: $output"

4
src/tools/punkres/PROVENANCE.md

@ -8,8 +8,8 @@
the punkshell repo root. That is a layout convention, not a requirement - if the punkshell repo root. That is a layout convention, not a requirement - if
no checkout is present there, clone the remote to that location; the Vendored no checkout is present there, clone the remote to that location; the Vendored
state commit below identifies the exact upstream state in any clone. state commit below identifies the exact upstream state in any clone.
- Vendored state: commit `c0eb919adf4bd542ff7b9827e49b4b7d791c51b6` (punkres - Vendored state: commit `dca8634f190d8e9e800637211d666951737909da` (punkres
v0.2.0, 2026-07-29; upstream working tree clean at copy time) v0.3.0, 2026-07-29; upstream working tree clean at copy time)
- Vendored: 2026-07-29 for goal G-128 (see - Vendored: 2026-07-29 for goal G-128 (see
`goals/G-128-portable-pe-resource-stamping.md`) `goals/G-128-portable-pe-resource-stamping.md`)
- Lineage: first-party punkshell tool, written 2026-07-29 for G-128 - no - Lineage: first-party punkshell tool, written 2026-07-29 for G-128 - no

12
src/tools/punkres/README.md

@ -26,7 +26,15 @@ Windows.
file-relative convention. file-relative convention.
- zip64 / multi-disk: always refused. - zip64 / multi-disk: always refused.
- non-zip overlay bytes: refused by default (their offset semantics are - non-zip overlay bytes: refused by default (their offset semantics are
unknowable); `-allow-opaque-overlay` moves them verbatim. unknowable). Two distinct consents: `-allow-opaque-overlay` RELOCATES
them verbatim (a self-locating payload such as a metakit vfs survives);
`-drop-opaque-overlay` DISCARDS them - correct only for expendable
trailing data such as the COFF symbol/debug tables mingw-built runtimes
carry after their sections (a real payload would be destroyed). A drop
that removes the region the COFF `PointerToSymbolTable` points into also
zeroes that pointer and `NumberOfSymbols` instead of leaving them
dangling (which is what Windows' own `EndUpdateResource` leaves behind
when it strips trailing data).
- Every `set-icon` run re-parses the file it just built and verifies the - Every `set-icon` run re-parses the file it just built and verifies the
resources and the overlay before reporting success (read-back is built in). resources and the overlay before reporting success (read-back is built in).
- Deterministic: stamping the same input twice yields byte-identical output. - Deterministic: stamping the same input twice yields byte-identical output.
@ -38,7 +46,7 @@ Windows.
punkres extract-ico [-group <id|name>] <pefile> <out.ico> punkres extract-ico [-group <id|name>] <pefile> <out.ico>
punkres set-icon [-o <outfile>] [-group <id|name>] punkres set-icon [-o <outfile>] [-group <id|name>]
[-allow-file-relative-shift] [-allow-opaque-overlay] [-allow-file-relative-shift] [-allow-opaque-overlay]
[-strip-signature] <pefile> <icon.ico> [-drop-opaque-overlay] [-strip-signature] <pefile> <icon.ico>
punkres version punkres version
`set-icon` follows the semantics of punkshell's twapi arm exactly: ALL existing `set-icon` follows the semantics of punkshell's twapi arm exactly: ALL existing

2
src/tools/punkres/build.zig.zon

@ -1,6 +1,6 @@
.{ .{
.name = .punkres, .name = .punkres,
.version = "0.2.0", .version = "0.3.0",
.fingerprint = 0x663d2953305aee5d, .fingerprint = 0x663d2953305aee5d,
.minimum_zig_version = "0.16.0", .minimum_zig_version = "0.16.0",
.paths = .{""}, .paths = .{""},

59
src/tools/punkres/src/punkres.zig

@ -10,7 +10,13 @@
//are moved verbatim; file-relative ones are REFUSED by default and stamped //are moved verbatim; file-relative ones are REFUSED by default and stamped
//only with -allow-file-relative-shift (offsets rewritten by the shift delta, //only with -allow-file-relative-shift (offsets rewritten by the shift delta,
//convention preserved); zip64/multi-disk are refused always; non-zip overlay //convention preserved); zip64/multi-disk are refused always; non-zip overlay
//bytes need -allow-opaque-overlay. //bytes need explicit consent - -allow-opaque-overlay RELOCATES them verbatim
//(a real payload, e.g. metakit, survives if it self-locates), while
//-drop-opaque-overlay DISCARDS them (correct only for expendable trailing
//data such as the COFF symbol/debug tables mingw-built runtimes carry after
//their sections; a real payload would be destroyed). When a drop removes the
//region the COFF PointerToSymbolTable points into, that pointer and
//NumberOfSymbols are zeroed rather than left dangling.
// //
//Exit codes: 0 ok, 1 error, 2 hard refusal (zip64/multi-disk/odd signature), //Exit codes: 0 ok, 1 error, 2 hard refusal (zip64/multi-disk/odd signature),
//3 refusal that a consent flag can lift (stated in the message). //3 refusal that a consent flag can lift (stated in the message).
@ -34,10 +40,10 @@ var g_io: Io = undefined;
//stdout with honestly-probed flags - set at entry (see stdout_file) //stdout with honestly-probed flags - set at entry (see stdout_file)
var g_stdout: Io.File = undefined; var g_stdout: Io.File = undefined;
pub const VERSION = "0.2.0"; pub const VERSION = "0.3.0";
pub const HEADER = pub const HEADER =
\\# PUNKRES 0.2.0 -- portable PE resource stamper (punkshell G-128) \\# PUNKRES 0.3.0 -- portable PE resource stamper (punkshell G-128)
\\ \\
; ;
@ -117,7 +123,7 @@ fn print_usage(exe: []const u8) !void {
\\ {s} extract-ico [-group <id|name>] <pefile> <out.ico> \\ {s} extract-ico [-group <id|name>] <pefile> <out.ico>
\\ {s} set-icon [-o <outfile>] [-group <id|name>] \\ {s} set-icon [-o <outfile>] [-group <id|name>]
\\ [-allow-file-relative-shift] [-allow-opaque-overlay] \\ [-allow-file-relative-shift] [-allow-opaque-overlay]
\\ [-strip-signature] <pefile> <icon.ico> \\ [-drop-opaque-overlay] [-strip-signature] <pefile> <icon.ico>
\\ {s} version \\ {s} version
\\ \\
\\ set-icon replaces ALL RT_ICON/RT_GROUP_ICON resources with the images \\ set-icon replaces ALL RT_ICON/RT_GROUP_ICON resources with the images
@ -125,8 +131,12 @@ fn print_usage(exe: []const u8) !void {
\\ language; name 1 / lang 1033 when none). -group overrides the group \\ language; name 1 / lang 1033 when none). -group overrides the group
\\ NAME (stored as given; lookups are case-insensitive) - language and \\ NAME (stored as given; lookups are case-insensitive) - language and
\\ codepage adoption are unchanged. Every other resource type and any \\ codepage adoption are unchanged. Every other resource type and any
\\ appended payload are preserved. Without -o the file is updated in \\ appended payload are preserved. Non-zip trailing bytes:
\\ place. Exit 3 = refused, a consent flag applies. \\ -allow-opaque-overlay RELOCATES them (a self-locating payload such
\\ as metakit survives); -drop-opaque-overlay DISCARDS them (only for
\\ expendable data such as COFF symbol tables - a real payload would
\\ be destroyed). Without -o the file is updated in place.
\\ Exit 3 = refused, a consent flag applies.
\\ \\
, .{ tail, tail, tail, tail, tail }); , .{ tail, tail, tail, tail, tail });
} }
@ -540,10 +550,15 @@ const SetIconFlags = struct {
group_arg: ?[]const u8 = null, group_arg: ?[]const u8 = null,
allow_file_relative_shift: bool = false, allow_file_relative_shift: bool = false,
allow_opaque_overlay: bool = false, allow_opaque_overlay: bool = false,
drop_opaque_overlay: bool = false,
strip_signature: bool = false, strip_signature: bool = false,
}; };
fn cmd_set_icon(pe_path: []const u8, ico_path: []const u8, flags: SetIconFlags) !u8 { fn cmd_set_icon(pe_path: []const u8, ico_path: []const u8, flags: SetIconFlags) !u8 {
if (flags.allow_opaque_overlay and flags.drop_opaque_overlay) {
errprintf("choose ONE of -allow-opaque-overlay (relocate) or -drop-opaque-overlay (discard) - they are opposite treatments of the same bytes.\n", .{});
return 1;
}
var data = try read_file(pe_path); var data = try read_file(pe_path);
const pe = pemod.Pe.parse(allocator, data) catch |err| { const pe = pemod.Pe.parse(allocator, data) catch |err| {
errprintf("{s}: not a usable PE image: {}\n", .{ pe_path, err }); errprintf("{s}: not a usable PE image: {}\n", .{ pe_path, err });
@ -575,16 +590,21 @@ fn cmd_set_icon(pe_path: []const u8, ico_path: []const u8, flags: SetIconFlags)
} }
//overlay classification and consent gates //overlay classification and consent gates
const rep = try classifyOverlay(data, pe.overlay_offset, file_end); var rep = try classifyOverlay(data, pe.overlay_offset, file_end);
var dropped_bytes: usize = 0;
switch (rep.kind) { switch (rep.kind) {
.zip_refused => { .zip_refused => {
errprintf("REFUSED: appended zip payload is {s} - punkres does not process zip64/multi-disk archives (no consent flag lifts this).\n", .{rep.refused_reason}); errprintf("REFUSED: appended zip payload is {s} - punkres does not process zip64/multi-disk archives (no consent flag lifts this).\n", .{rep.refused_reason});
return 2; return 2;
}, },
.data => { .data => {
if (!flags.allow_opaque_overlay) { if (flags.drop_opaque_overlay) {
dropped_bytes = rep.size;
file_end = pe.overlay_offset;
rep = .{ .kind = .none, .size = 0 };
} else if (!flags.allow_opaque_overlay) {
errprintf("REFUSED: {d} bytes of non-zip overlay follow the PE image. punkres cannot know whether their internal offsets survive being moved.\n", .{rep.size}); errprintf("REFUSED: {d} bytes of non-zip overlay follow the PE image. punkres cannot know whether their internal offsets survive being moved.\n", .{rep.size});
errprintf("Remedy: if the payload is safe to relocate verbatim (or offset-free), re-run with -allow-opaque-overlay.\n", .{}); errprintf("Remedy: -allow-opaque-overlay RELOCATES them verbatim (a self-locating payload such as metakit survives); -drop-opaque-overlay DISCARDS them (only for expendable trailing data such as COFF symbol/debug tables - a real payload would be destroyed).\n", .{});
return 3; return 3;
} }
}, },
@ -684,7 +704,23 @@ fn cmd_set_icon(pe_path: []const u8, ico_path: []const u8, flags: SetIconFlags)
try printf("overlay: {d} opaque bytes moved verbatim by {d} (consented)\n", .{ overlay_len, delta }); try printf("overlay: {d} opaque bytes moved verbatim by {d} (consented)\n", .{ overlay_len, delta });
} }
} else { } else {
try print("overlay: none\n"); if (dropped_bytes > 0) {
try printf("overlay: {d} bytes of non-zip trailing data dropped (consented) - nothing appended\n", .{dropped_bytes});
} else {
try print("overlay: none\n");
}
}
//a drop that removed the region the COFF symbol table lives in must not
//leave the header pointing past EOF (twapi's EndUpdateResource leaves it
//dangling; punkres zeroes it honestly)
if (dropped_bytes > 0) {
const psym = mem.readInt(u32, final[pe.coff_offset + 8 ..][0..4], .little);
const nsym = mem.readInt(u32, final[pe.coff_offset + 12 ..][0..4], .little);
if (psym != 0 and psym >= pe.overlay_offset) {
mem.writeInt(u32, final[pe.coff_offset + 8 ..][0..4], 0, .little);
mem.writeInt(u32, final[pe.coff_offset + 12 ..][0..4], 0, .little);
try printf("coff_symbols: symbol table ({d} entries) was in the dropped region - header pointer zeroed\n", .{nsym});
}
} }
if (stripped_signature) { if (stripped_signature) {
//zero the security directory in the OUTPUT (table bytes were dropped) //zero the security directory in the OUTPUT (table bytes were dropped)
@ -853,6 +889,9 @@ pub fn main(init: std.process.Init) !u8 {
} else if (mem.eql(u8, argv[i], "-allow-opaque-overlay")) { } else if (mem.eql(u8, argv[i], "-allow-opaque-overlay")) {
flags.allow_opaque_overlay = true; flags.allow_opaque_overlay = true;
i += 1; i += 1;
} else if (mem.eql(u8, argv[i], "-drop-opaque-overlay")) {
flags.drop_opaque_overlay = true;
i += 1;
} else if (mem.eql(u8, argv[i], "-strip-signature")) { } else if (mem.eql(u8, argv[i], "-strip-signature")) {
flags.strip_signature = true; flags.strip_signature = true;
i += 1; i += 1;

82
src/tools/punkres/src/punkres_test.zig

@ -187,7 +187,7 @@ test "print usage" {
\\ punkres.exe extract-ico [-group <id|name>] <pefile> <out.ico> \\ punkres.exe extract-ico [-group <id|name>] <pefile> <out.ico>
\\ punkres.exe set-icon [-o <outfile>] [-group <id|name>] \\ punkres.exe set-icon [-o <outfile>] [-group <id|name>]
\\ [-allow-file-relative-shift] [-allow-opaque-overlay] \\ [-allow-file-relative-shift] [-allow-opaque-overlay]
\\ [-strip-signature] <pefile> <icon.ico> \\ [-drop-opaque-overlay] [-strip-signature] <pefile> <icon.ico>
\\ punkres.exe version \\ punkres.exe version
\\ \\
\\ set-icon replaces ALL RT_ICON/RT_GROUP_ICON resources with the images \\ set-icon replaces ALL RT_ICON/RT_GROUP_ICON resources with the images
@ -195,8 +195,12 @@ test "print usage" {
\\ language; name 1 / lang 1033 when none). -group overrides the group \\ language; name 1 / lang 1033 when none). -group overrides the group
\\ NAME (stored as given; lookups are case-insensitive) - language and \\ NAME (stored as given; lookups are case-insensitive) - language and
\\ codepage adoption are unchanged. Every other resource type and any \\ codepage adoption are unchanged. Every other resource type and any
\\ appended payload are preserved. Without -o the file is updated in \\ appended payload are preserved. Non-zip trailing bytes:
\\ place. Exit 3 = refused, a consent flag applies. \\ -allow-opaque-overlay RELOCATES them (a self-locating payload such
\\ as metakit survives); -drop-opaque-overlay DISCARDS them (only for
\\ expendable data such as COFF symbol tables - a real payload would
\\ be destroyed). Without -o the file is updated in place.
\\ Exit 3 = refused, a consent flag applies.
\\ \\
; ;
try expectExecute(&args, 1, output, tmp); try expectExecute(&args, 1, output, tmp);
@ -206,7 +210,7 @@ test "version" {
var tmp = initTestDir(); var tmp = initTestDir();
defer tmp.cleanup(); defer tmp.cleanup();
const args = [_][]const u8{"version"}; const args = [_][]const u8{"version"};
try expectExecute(&args, 0, "punkres 0.2.0\n", tmp); try expectExecute(&args, 0, "punkres 0.3.0\n", tmp);
} }
test "set-icon -group renames the group; later stamps adopt it (convergent)" { test "set-icon -group renames the group; later stamps adopt it (convergent)" {
@ -472,6 +476,76 @@ test "opaque non-zip overlay: refused by default, moved verbatim with consent" {
try expect(mem.eql(u8, out[out.len - blob.len ..], blob)); try expect(mem.eql(u8, out[out.len - blob.len ..], blob));
} }
test "drop-opaque-overlay discards expendable trailing data and zeroes the dangling symbol pointer" {
//the shape mingw-built runtimes hand the stub-stamping seam: PE image
//followed by its COFF symbol/debug tables (located by the COFF header
//pointer, outside every section). -drop discards them; the output must be
//byte-identical to stamping the same image with no trailing data at all
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
var tmp = initTestDir();
defer tmp.cleanup();
const img = try synthpe.buildPe(.{ .alloc = alloc });
try writeTmp(tmp, "clean.exe", img);
const junk = "FAKE-COFF-SYMBOL-TABLE-DATA .file g-crtbegin.c ...............";
const dirty = try alloc.alloc(u8, img.len + junk.len);
@memcpy(dirty[0..img.len], img);
@memcpy(dirty[img.len..], junk);
//point the COFF symbol-table header fields at the trailing region
//(e_lfanew 0x80 -> COFF header at 0x84 -> PointerToSymbolTable at +8)
std.mem.writeInt(u32, dirty[0x8c..][0..4], @intCast(img.len), .little);
std.mem.writeInt(u32, dirty[0x90..][0..4], 12332, .little);
//keep the stored checksum honest after the header patch (parse tolerates
//any checksum; equality with the clean stamp is what matters below)
try writeTmp(tmp, "dirty.exe", dirty);
const test_ico = try synthpe.buildTestIco(alloc, 2);
try writeTmp(tmp, "test.ico", test_ico);
//refusal without a flag names BOTH remedies
const refuse_args = [_][]const u8{ "set-icon", "-o", "dout.exe", "dirty.exe", "test.ico" };
try expectExecuteContains(&refuse_args, 3, &[_][]const u8{}, &[_][]const u8{
"-allow-opaque-overlay RELOCATES",
"-drop-opaque-overlay DISCARDS",
}, tmp);
//conflicting consent flags are rejected
const conflict_args = [_][]const u8{ "set-icon", "-o", "dout.exe", "-allow-opaque-overlay", "-drop-opaque-overlay", "dirty.exe", "test.ico" };
try expectExecuteContains(&conflict_args, 1, &[_][]const u8{}, &[_][]const u8{
"choose ONE",
}, tmp);
//drop: stamps, reports the drop and the symbol-pointer zeroing
const drop_args = [_][]const u8{ "set-icon", "-o", "dout.exe", "-drop-opaque-overlay", "dirty.exe", "test.ico" };
try expectExecuteContains(&drop_args, 0, &[_][]const u8{
"bytes of non-zip trailing data dropped (consented)",
"coff_symbols: symbol table (12332 entries) was in the dropped region - header pointer zeroed",
"verify: resources re-read OK; no overlay",
}, &[_][]const u8{}, tmp);
//byte-identical to stamping the trailing-free image
const clean_args = [_][]const u8{ "set-icon", "-o", "cout.exe", "clean.exe", "test.ico" };
try expectExecuteContains(&clean_args, 0, &[_][]const u8{}, &[_][]const u8{}, tmp);
const dout = try readTmp(alloc, tmp, "dout.exe");
const cout = try readTmp(alloc, tmp, "cout.exe");
try expect(mem.eql(u8, dout, cout));
//-drop alongside a ZIP overlay is inert: the payload is preserved
const entries = [_]synthpe.ZipEntry{.{ .name = "p.txt", .data = "payload" }};
const zip_bytes = try synthpe.buildStoredZip(alloc, &entries, .archive, 0, "");
const zkit = try alloc.alloc(u8, img.len + zip_bytes.len);
@memcpy(zkit[0..img.len], img);
@memcpy(zkit[img.len..], zip_bytes);
try writeTmp(tmp, "zkit.exe", zkit);
const zdrop_args = [_][]const u8{ "set-icon", "-o", "zout.exe", "-drop-opaque-overlay", "zkit.exe", "test.ico" };
try expectExecuteContains(&zdrop_args, 0, &[_][]const u8{
"bytes preserved verbatim",
}, &[_][]const u8{}, tmp);
const zout = try readTmp(alloc, tmp, "zout.exe");
try expect(mem.eql(u8, zout[zout.len - zip_bytes.len ..], zip_bytes));
}
test "archive-relative zip preceded by other overlay bytes (nested-payload shape) stamps and verifies" { test "archive-relative zip preceded by other overlay bytes (nested-payload shape) stamps and verifies" {
//the punkshell tclsfe stub carries its own zip payload; concatenating a //the punkshell tclsfe stub carries its own zip payload; concatenating a
//further zip (the zipcat shape built from a raw stub) yields an overlay //further zip (the zipcat shape built from a raw stub) yields an overlay

Loading…
Cancel
Save