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 {
|
||||
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
|
||||
switch mnem {
|
||||
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
|
||||
// fast path: ANDS ZR, Rn, #imm is their form.
|
||||
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 {
|
||||
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 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
|
||||
@@ -3780,12 +3788,12 @@ func encodeARM64Pair(mnem string, baseOp uint32, ops []*ast.Operand, pc int, fi
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s expects a register pair (Rt1, Rt2)", mnem)
|
||||
}
|
||||
// Constrained unpredictable: the pair registers differ, and a
|
||||
// writeback base rides no pair member.
|
||||
if rt1 == rt2 {
|
||||
// Constrained unpredictable: the pair registers differ (the ZR pair is
|
||||
// the toolchain's own idiom), and a writeback base rides no pair member.
|
||||
if rt1 == rt2 && rt1 != 31 {
|
||||
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)) {
|
||||
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 {
|
||||
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]))
|
||||
if !ok || end+2 != len(ops) || vd.hasIdx {
|
||||
return nil, fmt.Errorf("invalid destination register in VTBL")
|
||||
|
||||
@@ -2108,7 +2108,6 @@ func TestArm64FPImmediate(t *testing.T) {
|
||||
"\tFMOVD\t$5, F0\n",
|
||||
"\tFMOVS\t$4, 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")
|
||||
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