Skip to content

fix: restore dead-method elimination with obfuscated reflect - #1157

Merged
luantak merged 3 commits into
burrowers:masterfrom
luames:fix/reflect-dead-methods
Oct 6, 2026
Merged

luantak merged 3 commits into
burrowers:masterfrom
luames:fix/reflect-dead-methods

Conversation

@luames

@luames luames commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1156. The compiler's usemethod exemption matches reflect's own method implementations by receiver-qualified symbol name. Renaming rtype and interfaceType breaks that exemption, marks those implementations with AttrReflectMethod, and makes the linker retain every exported method of reachable types.

Preserve both receiver names. This also includes the Value preservation from #1150 for #1149, since restoring dead-method elimination without it can expose unreachable-method crashes in genuine reflection callers. The PR targets master and does not include the other fixes from #1150.

Extend the existing reflection fixture with a separate minimal program that looks up a constant method name on concrete and interface types, executes the concrete method, and checks that an unrelated method's marker is absent. Keeping this separate from the fixture's main program prevents its dynamic reflection calls from legitimately retaining every method. The test commit records the old behavior; the fix commit flips the assertion.

Linkname rewriting also respects preserved receiver names for both pointer and value methods. The existing rtype linknames and an added Value.IsValid call cover those cases.

@APshenkin is credited as a co-author on all commits for the diagnosis, reproducer, and proposed fix.

Tests

  • go test -run '^TestScript$/^(reflect|linkname)$' -count=1 passes without short mode, including the plain-build comparisons.
  • The new absence assertion fails on unchanged master. Removing only interfaceType preservation also makes it fail.
  • go vet ./... and git diff --check pass. Changed Go source and archived Go entries are gofmt-clean.
  • CI passes on the final head, including full Linux, macOS, and Windows tests, short 386 and race tests, third-party builds, and Linux static checks.

Implemented by Hermes Agent using gpt-6.1-sol, acting on behalf of @luantak.

luantak and others added 3 commits October 6, 2026 21:40
A constant MethodByName lookup should retain only the named method,
but obfuscating reflect currently makes the linker retain unrelated
exported methods as well. Record the current behavior in the existing
reflection fixture with a separate minimal program, so its other dynamic
reflection calls cannot mask the regression.

The correct assertion fails on master with:
  unexpected match for ["unused-method-marker"] in methods-bin

Refs burrowers#1156.

Co-authored-by: Andrey Pshenkin <andrey.pshenkin@gmail.com>
The compiler exempts reflect own Method implementations by exact
receiver-qualified symbol names. Obfuscating rtype and interfaceType
breaks that exemption, so the linker sees reflection and retains every
exported method of reachable types, even for constant MethodByName calls.

Preserve both receiver names and assert that an unrelated method body
is absent while the selected method still executes. Exercise interface
lookups too; removing interfaceType preservation fails the same check.
The original code fails with:
  unexpected match for ["unused-method-marker"] in methods-bin

Also preserve Value, as proposed in burrowers#1150 for burrowers#1149. Restoring method
elimination without recognizing genuine Value.Method callers can expose
unreachable-method crashes previously masked by excessive retention.

Fixes burrowers#1156.

Co-authored-by: Andrey Pshenkin <andrey.pshenkin@gmail.com>
Preserving reflect receiver names exposed unconditional receiver hashing
in linkname rewriting. The existing linkname fixture then failed with:
  relocation target reflect.(*rZMjqBiHF6Ld).NumMethod not defined

Apply the toolchain-name exemption to both pointer and value receiver
spellings while continuing to obfuscate ordinary receivers and method
names. The existing rtype links cover the pointer case; add a linknamestd
call to Value.IsValid to exercise the value case in the same build.

Refs burrowers#1156.

Co-authored-by: Andrey Pshenkin <andrey.pshenkin@gmail.com>
@luantak
luantak merged commit c508821 into burrowers:master Oct 6, 2026
6 checks passed
luames pushed a commit to luames/garble that referenced this pull request Oct 8, 2026
The reflect.Value name preservation for burrowers#1149 landed in burrowers#1157, but
Daniel Martí's direct Value.Method(...).Call regression from burrowers#1150
was not included.

Exercise the call in the existing reflection program and add its
output to the shared golden file. This reuses both the garbled build
and the plain Go comparison without adding a separate build. Retain
the upstream reflected-recovery and method-elimination assertions.

Fixes burrowers#1149.

Co-authored-by: Daniel Martí <mvdan@mvdan.cc>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

GOGARBLE=* disables dead-method elimination: reflect's own Method implementations get AttrReflectMethod

2 participants