fix(asm): reject duplicate symbol declarations like the toolchain
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
a69f8cf4a8
commit
c107b45933
2 files changed
+154
-4
No files matched your search
@@ -12,9 +12,107 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/ast"
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/parser"
|
||||
)
|
||||
|
||||
// TestDuplicateDecls pins the duplicate-declaration rule against the
|
||||
// toolchain's, measured with go tool asm (Go 1.27.1): a symbol declared
|
||||
// twice in one file is rejected at the second declaration whether the pair
|
||||
// is two TEXTs, two GLOBLs or one of each, and DUPOK never legalises it
|
||||
// within a file (the toolchain's duperror.s testdata asserts the plain
|
||||
// pair). DUPOK legalises duplicates across files, so each file of a pair
|
||||
// assembles cleanly on its own, and symbol identity keeps the package
|
||||
// prefix and the <> static marker: foo(SB), foo<>(SB) and other·foo(SB)
|
||||
// are three symbols, not one. Every case runs through all four
|
||||
// per-architecture entry points.
|
||||
func TestDuplicateDecls(t *testing.T) {
|
||||
targets := []struct {
|
||||
goarch string
|
||||
file string
|
||||
asm func(*ast.File) (*Image, error)
|
||||
}{
|
||||
{"amd64", "dup_amd64.s", func(f *ast.File) (*Image, error) { return AssembleFile(f) }},
|
||||
{"arm64", "dup_arm64.s", AssembleFileARM64},
|
||||
{"riscv64", "dup_riscv64.s", AssembleFileRISCV},
|
||||
{"loong64", "dup_loong64.s", AssembleFileLOONG64},
|
||||
}
|
||||
cases := []struct {
|
||||
name string
|
||||
src string
|
||||
want string // substring of the error, "" for no error
|
||||
}{
|
||||
{
|
||||
"plain TEXT duplicate rejected",
|
||||
"TEXT foo(SB), NOSPLIT, $0\n\tRET\nTEXT foo(SB), NOSPLIT, $0\n\tRET\n",
|
||||
`symbol "foo" redeclared (other declaration on line 1)`,
|
||||
},
|
||||
{
|
||||
"DUPOK pair in one file rejected",
|
||||
"TEXT foo(SB), DUPOK, $0\n\tRET\nTEXT foo(SB), DUPOK, $0\n\tRET\n",
|
||||
`symbol "foo" redeclared (other declaration on line 1)`,
|
||||
},
|
||||
{
|
||||
"TEXT then GLOBL rejected",
|
||||
"TEXT foo(SB), NOSPLIT, $0\n\tRET\nGLOBL foo(SB), NOPTR, $8\n",
|
||||
`symbol "foo" redeclared`,
|
||||
},
|
||||
{
|
||||
"GLOBL then TEXT rejected",
|
||||
"GLOBL foo(SB), NOPTR, $8\nTEXT foo(SB), NOSPLIT, $0\n\tRET\n",
|
||||
`symbol "foo" redeclared`,
|
||||
},
|
||||
{
|
||||
"plain GLOBL duplicate rejected",
|
||||
"GLOBL bar(SB), NOPTR, $8\nGLOBL bar(SB), NOPTR, $8\n",
|
||||
`symbol "bar" redeclared`,
|
||||
},
|
||||
{
|
||||
"static and package markers separate symbols",
|
||||
"TEXT foo(SB), NOSPLIT, $0\n\tRET\nTEXT foo<>(SB), NOSPLIT, $0\n\tRET\nTEXT other\u00b7foo(SB), NOSPLIT, $0\n\tRET\n",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"single DUPOK TEXT assembles",
|
||||
"TEXT foo(SB), DUPOK, $0\n\tRET\n",
|
||||
"",
|
||||
},
|
||||
}
|
||||
for _, tg := range targets {
|
||||
for _, c := range cases {
|
||||
f, errs := parser.Parse(tg.file, c.src)
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("%s/%s: parse: %v", tg.goarch, c.name, errs)
|
||||
}
|
||||
_, err := tg.asm(f)
|
||||
if c.want == "" {
|
||||
if err != nil {
|
||||
t.Errorf("%s/%s: error %v, want none", tg.goarch, c.name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err == nil || !strings.Contains(err.Error(), c.want) {
|
||||
t.Errorf("%s/%s: error %v, want substring %q", tg.goarch, c.name, err, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A DUPOK pair across files is the case DUPOK exists for: the linker
|
||||
// resolves it, the assembler never sees both sides, so each file of the
|
||||
// pair assembles cleanly on its own.
|
||||
for _, tg := range targets {
|
||||
for _, name := range []string{"first", "second"} {
|
||||
f, errs := parser.Parse(tg.file, "TEXT foo(SB), DUPOK, $0\n\tRET\n")
|
||||
if len(errs) > 0 {
|
||||
t.Fatalf("%s/dupok cross-file %s: parse: %v", tg.goarch, name, errs)
|
||||
}
|
||||
if _, err := tg.asm(f); err != nil {
|
||||
t.Errorf("%s/dupok cross-file %s: error %v, want none", tg.goarch, name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAssembleFileStaticData checks the whole-image layout; code, padding
|
||||
// and the data section; and that the RIP-relative displacements of static
|
||||
// symbol loads resolve to the right bytes.
|
||||
|
||||
Reference in new issue
Block a user