From ddb84403402d6cfc7ad86284acc60c2962e1d8ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Sun, 20 Sep 2026 00:38:24 +0200 Subject: [PATCH] fix(cmd): padding-aware ground-truth comparison Assisted-by: GLM 5.3 --- cmd/gasm/main.go | 35 ++++++++++++++++++++++++++++++++--- cmd/gasm/main_test.go | 20 ++++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/cmd/gasm/main.go b/cmd/gasm/main.go index cffc814..61b6018 100644 --- a/cmd/gasm/main.go +++ b/cmd/gasm/main.go @@ -938,11 +938,40 @@ func compareGroundTruth(img *asm.Image, gt map[string][]byte) (matched, total, d goCmp[j] = 0 } } - if bytes.Equal(gasmCmp, goCmp) { + // The toolchain pads text symbols to 16-byte boundaries with + // zeros, so a function whose size is not a multiple of 16 + // carries trailing zeros in the ground truth that are not part + // of the encoding. Compare up to the shorter side and require + // the remainder of whichever is longer to be zero, so padding + // never masks a real difference. + cmpLen := min(len(gasmCmp), len(goCmp)) + equal := bytes.Equal(gasmCmp[:cmpLen], goCmp[:cmpLen]) + if equal { + for _, b := range gasmCmp[cmpLen:] { + if b != 0 { + equal = false + break + } + } + } + if equal { + for _, b := range goCmp[cmpLen:] { + if b != 0 { + equal = false + break + } + } + } + if equal { matched++ - if len(fn.Relocs) > 0 { + switch { + case len(fn.Relocs) > 0 && len(goCmp) > cmpLen: + fmt.Printf(" %s: MATCH (%d bytes, %d relocs masked, %d padding)\n", fn.Name, fn.Size, len(fn.Relocs), len(goCmp)-cmpLen) + case len(fn.Relocs) > 0: fmt.Printf(" %s: MATCH (%d bytes, %d relocs masked)\n", fn.Name, fn.Size, len(fn.Relocs)) - } else { + case len(goCmp) > cmpLen: + fmt.Printf(" %s: MATCH (%d bytes, %d padding)\n", fn.Name, fn.Size, len(goCmp)-cmpLen) + default: fmt.Printf(" %s: MATCH (%d bytes)\n", fn.Name, fn.Size) } } else { diff --git a/cmd/gasm/main_test.go b/cmd/gasm/main_test.go index 318633f..0f6bf4b 100644 --- a/cmd/gasm/main_test.go +++ b/cmd/gasm/main_test.go @@ -15,6 +15,7 @@ import ( "testing" "sourcedock.dev/petrbalvin/gasm-devkit/arch" + "sourcedock.dev/petrbalvin/gasm-devkit/asm" ) const clean = "#include \"textflag.h\"\n" + @@ -440,3 +441,22 @@ func TestRunCorpusAudit(t *testing.T) { t.Errorf("arm64 unencodable reasons = %d, want 1", r) } } + +// TestCompareGroundTruthPadding pins the padding-aware ground-truth +// comparison: the toolchain pads text symbols to 16-byte boundaries, so +// trailing zeros in the reference must not read as a mismatch, while any +// non-zero tail still must. +func TestCompareGroundTruthPadding(t *testing.T) { + code := []byte{0x48, 0x8b, 0x07, 0xc3} // 4 bytes, not a multiple of 16 + img := &asm.Image{Code: code, Funcs: []asm.FuncLayout{{Name: "f", Offset: 0, Size: len(code)}}} + padded := append(append([]byte(nil), code...), 0, 0, 0) + matched, total, diffs := compareGroundTruth(img, map[string][]byte{"f": padded}) + if matched != 1 || total != 1 || diffs != 0 { + t.Fatalf("zero padding should match: matched=%d total=%d diffs=%d", matched, total, diffs) + } + dirty := append(append([]byte(nil), code...), 0, 0x90, 0) + matched, _, diffs = compareGroundTruth(img, map[string][]byte{"f": dirty}) + if matched != 0 || diffs != 1 { + t.Fatalf("non-zero padding must mismatch: matched=%d diffs=%d", matched, diffs) + } +}