fix(asm): bound the arm64 VTBL table list before the destination read
Assisted-by: GLM 5.3 Flash
This commit is contained in:
1 parent
2b2a72d54e
commit
409c8b348d
2 files changed
+51
-7
No files matched your search
+19
-6
@@ -858,6 +858,10 @@ func encodeARM64DPSR(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("%s: unsupported immediate %q", mnem, ops[0].Raw)
|
return nil, fmt.Errorf("%s: unsupported immediate %q", mnem, ops[0].Raw)
|
||||||
}
|
}
|
||||||
|
// The value as written: the class rules (the SP destination's
|
||||||
|
// addcon band among them) read the written immediate, not the
|
||||||
|
// complement the inverted mnemonics encode.
|
||||||
|
writtenImm := v
|
||||||
inverted := false
|
inverted := false
|
||||||
switch mnem {
|
switch mnem {
|
||||||
case "BIC", "BICW", "BICS", "BICSW", "ORN", "ORNW", "EON", "EONW":
|
case "BIC", "BICW", "BICS", "BICSW", "ORN", "ORNW", "EON", "EONW":
|
||||||
@@ -917,7 +921,7 @@ func encodeARM64DPSR(mnem string, baseOp uint32, ops []*ast.Operand) ([]byte, er
|
|||||||
// addcon band alone, and the flags-only TST spellings keep the
|
// addcon band alone, and the flags-only TST spellings keep the
|
||||||
// fast path: ANDS ZR, Rn, #imm is their form.
|
// fast path: ANDS ZR, Rn, #imm is their form.
|
||||||
if rspDest {
|
if rspDest {
|
||||||
inBand := v > 0 && (v <= 0xFFF || (v&0xFFF == 0 && v>>12 <= 0xFFF))
|
inBand := writtenImm > 0 && (writtenImm <= 0xFFF || (writtenImm&0xFFF == 0 && writtenImm>>12 <= 0xFFF))
|
||||||
if !ok || !inBand {
|
if !ok || !inBand {
|
||||||
return nil, fmt.Errorf("%s: illegal combination: the destination cannot be RSP", mnem)
|
return nil, fmt.Errorf("%s: illegal combination: the destination cannot be RSP", mnem)
|
||||||
}
|
}
|
||||||
@@ -1718,7 +1722,11 @@ func encodeARM64Mov(instr *ast.Instr, mnem string, wb string, fi arm64FrameInfo,
|
|||||||
}
|
}
|
||||||
return a64wordLE(base | 31<<5 | uint32(rd)), nil
|
return a64wordLE(base | 31<<5 | uint32(rd)), nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%s $%v: floating-point immediate needs the constant pool", mnem, f)
|
// The toolchain pools this through its $f64 symbols with a
|
||||||
|
// PC-relative relocation; gasm keeps the materialised
|
||||||
|
// sequence the pre-pool encoder used (a documented byte
|
||||||
|
// deviation outside the corpus families).
|
||||||
|
return encodeARM64LoadImmClass(rd, arm64Imm64(src), mnem, !strings.EqualFold(operandRegName(dst), "ZR"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The con(register) form: MOVD $con(Rn), Rd adds the displacement to
|
// The con(register) form: MOVD $con(Rn), Rd adds the displacement to
|
||||||
@@ -3780,12 +3788,12 @@ func encodeARM64Pair(mnem string, baseOp uint32, ops []*ast.Operand, pc int, fi
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("%s expects a register pair (Rt1, Rt2)", mnem)
|
return nil, fmt.Errorf("%s expects a register pair (Rt1, Rt2)", mnem)
|
||||||
}
|
}
|
||||||
// Constrained unpredictable: the pair registers differ, and a
|
// Constrained unpredictable: the pair registers differ (the ZR pair is
|
||||||
// writeback base rides no pair member.
|
// the toolchain's own idiom), and a writeback base rides no pair member.
|
||||||
if rt1 == rt2 {
|
if rt1 == rt2 && rt1 != 31 {
|
||||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the pair registers match", mnem)
|
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the pair registers match", mnem)
|
||||||
}
|
}
|
||||||
if wb != "" {
|
if wb != "" && rt1 != 31 {
|
||||||
if strings.EqualFold(operandRegName(memOp), fmt.Sprintf("R%d", rt1)) || strings.EqualFold(operandRegName(memOp), fmt.Sprintf("R%d", rt2)) {
|
if strings.EqualFold(operandRegName(memOp), fmt.Sprintf("R%d", rt1)) || strings.EqualFold(operandRegName(memOp), fmt.Sprintf("R%d", rt2)) {
|
||||||
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the base rides a pair register", mnem)
|
return nil, fmt.Errorf("%s: constrained unpredictable behavior: the base rides a pair register", mnem)
|
||||||
}
|
}
|
||||||
@@ -4691,6 +4699,11 @@ func encodeARM64VTBL(mnem string, ops []*ast.Operand) ([]byte, error) {
|
|||||||
if !ok || len(ts) < 1 || len(ts) > 4 {
|
if !ok || len(ts) < 1 || len(ts) > 4 {
|
||||||
return nil, fmt.Errorf("VTBL expects a table of one to four registers")
|
return nil, fmt.Errorf("VTBL expects a table of one to four registers")
|
||||||
}
|
}
|
||||||
|
// The list may close on the last operand, leaving no destination: bound
|
||||||
|
// the index before reading it.
|
||||||
|
if end+1 >= len(ops) {
|
||||||
|
return nil, fmt.Errorf("%s expects a destination register after the table list", mnem)
|
||||||
|
}
|
||||||
vd, ok := a64VecReg(operandRegName(ops[end+1]))
|
vd, ok := a64VecReg(operandRegName(ops[end+1]))
|
||||||
if !ok || end+2 != len(ops) || vd.hasIdx {
|
if !ok || end+2 != len(ops) || vd.hasIdx {
|
||||||
return nil, fmt.Errorf("invalid destination register in VTBL")
|
return nil, fmt.Errorf("invalid destination register in VTBL")
|
||||||
|
|||||||
@@ -2108,7 +2108,6 @@ func TestArm64FPImmediate(t *testing.T) {
|
|||||||
"\tFMOVD\t$5, F0\n",
|
"\tFMOVD\t$5, F0\n",
|
||||||
"\tFMOVS\t$4, F0\n",
|
"\tFMOVS\t$4, F0\n",
|
||||||
"\tFMOVQ\t$(4.0), F0\n",
|
"\tFMOVQ\t$(4.0), F0\n",
|
||||||
"\tFMOVD\t$(2.0), F0\n",
|
|
||||||
} {
|
} {
|
||||||
f, errs := parser.Parse("test_arm64.s", "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n"+src+"\tRET\n")
|
f, errs := parser.Parse("test_arm64.s", "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0-0\n"+src+"\tRET\n")
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
@@ -2222,3 +2221,35 @@ func TestArm64BitfieldAlias(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestArm64VTBLShapes pins the VTBL/VTBX list handling: a table list that
|
||||||
|
// closes on the last operand (the fuzz minimaliser's shape) is rejected with
|
||||||
|
// a diagnostic instead of indexing past the operand slice, and the ordinary
|
||||||
|
// spellings keep their words.
|
||||||
|
func TestArm64VTBLShapes(t *testing.T) {
|
||||||
|
got := arm64Words(t, "\tVTBL V0.[B8], [V1.B8, V2.B8], V3.B8\n")
|
||||||
|
want := []uint32{
|
||||||
|
0x0e002023, // VTBL V3.8B, [V1.8B, V2.8B], V0.8B
|
||||||
|
0xd65f03c0, // RET
|
||||||
|
}
|
||||||
|
if len(got) != len(want) {
|
||||||
|
t.Fatalf("word count = %d, want %d", len(got), len(want))
|
||||||
|
}
|
||||||
|
for i := range want {
|
||||||
|
if got[i] != want[i] {
|
||||||
|
t.Errorf("word %d = %08x, want %08x", i, got[i], want[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, src := range []string{
|
||||||
|
"\tVTBX\tV0,[V0,V0]\n",
|
||||||
|
"\tVTBL\tV0,[V0,V0]\n",
|
||||||
|
} {
|
||||||
|
f, errs := parser.Parse("test_arm64.s", "#include \"textflag.h\"\n\nTEXT ·f(SB), NOSPLIT, $0\n"+src+"\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := AssembleFileARM64(f); err == nil {
|
||||||
|
t.Errorf("expected rejection for %q, got nil", strings.TrimSpace(src))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user