feat(disasm): name the arm64 words arm64asm refuses
arm64asm rejects three exception-space words the toolchain's corpus assembles: the hypervisor call HVC, the secure monitor call SMC and the speculation barrier SB. The supplementary naming table reads the raw word in the error branch and renders the corpus spellings; the corpus rows are pinned in the parity fixture and the boundary test holds the unallocated neighbours on the placeholder. Assisted-by: GLM 5.3
This commit is contained in:
1 parent
3f35b2a718
commit
0f6e008c9a
4 files changed
+114
No files matched your search
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package disasm
|
||||
|
||||
import "fmt"
|
||||
|
||||
// golang.org/x/arch refuses a corner of the arm64 exception-generation
|
||||
// space outright: the hypervisor call HVC, the secure monitor call SMC and
|
||||
// the speculation barrier SB. The toolchain's assembler corpus carries all
|
||||
// three with machine-checked encodings (arm64enc.s's "HVC $61428 //
|
||||
// 82fe1dd4" and its kin), so the disassembler names them from the raw word
|
||||
// in the error branch, the way the amd64 table does.
|
||||
//
|
||||
// The encodings are the exception space 1101 0100 0 opc imm16 op2: HVC is
|
||||
// opc 000 with the op2 marker 00010, SMC the same opc with 00011, and SB
|
||||
// the fixed barrier word 0xD50330FF. Every other shape in the space keeps
|
||||
// the placeholder.
|
||||
|
||||
// nameARM64Rejected names an arm64 word the decoder refuses. It reports
|
||||
// the rendered text, the instruction's length in bytes and whether the word
|
||||
// matched a family; a non-match keeps the placeholder.
|
||||
func nameARM64Rejected(code []byte) (string, int, bool) {
|
||||
w := uint32(code[0]) | uint32(code[1])<<8 | uint32(code[2])<<16 | uint32(code[3])<<24
|
||||
switch {
|
||||
case w == 0xd50330ff:
|
||||
return "SB", 4, true
|
||||
case w>>21 == 0x6a0:
|
||||
imm := (w >> 5) & 0xffff
|
||||
switch w & 0x1f {
|
||||
case 0x02:
|
||||
return fmt.Sprintf("HVC $%d", imm), 4, true
|
||||
case 0x03:
|
||||
return fmt.Sprintf("SMC $%d", imm), 4, true
|
||||
}
|
||||
}
|
||||
return "", 0, false
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package disasm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
|
||||
)
|
||||
|
||||
// TestRejectedNaming pins the supplementary arm64 naming table over the
|
||||
// corpus rows whose encodings the decoder refuses: every row's bytes are
|
||||
// the corpus's own expected-encoding comments (machine-checked by the
|
||||
// toolchain's assembler test suite) and every text is the corpus's own
|
||||
// spelling.
|
||||
func TestRejectedNaming(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
code []byte
|
||||
text string
|
||||
}{
|
||||
// arm64enc.s: HVC $61428 // 82fe1dd4 and SMC $37977 // 238b12d4.
|
||||
{[]byte{0x82, 0xfe, 0x1d, 0xd4}, "HVC $61428"},
|
||||
{[]byte{0x23, 0x8b, 0x12, 0xd4}, "SMC $37977"},
|
||||
// arm64enc.s: SB // ff3003d5.
|
||||
{[]byte{0xff, 0x30, 0x03, 0xd5}, "SB"},
|
||||
} {
|
||||
ins, err := Decode(arch.ARM64, tt.code, 0)
|
||||
if err != nil {
|
||||
t.Errorf("% x: %v", tt.code, err)
|
||||
continue
|
||||
}
|
||||
if ins.Text != tt.text || ins.Len != len(tt.code) {
|
||||
t.Errorf("% x: %q (%d bytes), want %q (%d)",
|
||||
tt.code, ins.Text, ins.Len, tt.text, len(tt.code))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestRejectedNamingBoundaries guards the table's edges: the exception
|
||||
// space shapes the corpus does not spell keep the placeholder, and the
|
||||
// neighbours the decoder names itself never reach the table.
|
||||
func TestRejectedNamingBoundaries(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
code []byte
|
||||
text string
|
||||
}{
|
||||
// The unallocated corners of the same opc group (op2 00000,
|
||||
// 00100 and 11000), which the corpus does not spell.
|
||||
{[]byte{0x00, 0x00, 0x00, 0xd4}, "???"},
|
||||
{[]byte{0x04, 0x00, 0x00, 0xd4}, "???"},
|
||||
{[]byte{0x60, 0x00, 0x00, 0xd4}, "???"},
|
||||
// BRK and HLT sit in the same space one opc step away and the
|
||||
// decoder names them itself.
|
||||
{[]byte{0x00, 0x00, 0x20, 0xd4}, "BRK $0"},
|
||||
{[]byte{0x00, 0x00, 0x40, 0xd4}, "HLT $0"},
|
||||
} {
|
||||
ins, err := Decode(arch.ARM64, tt.code, 0)
|
||||
if err != nil {
|
||||
t.Errorf("% x: %v", tt.code, err)
|
||||
continue
|
||||
}
|
||||
if ins.Text != tt.text {
|
||||
t.Errorf("% x: %q, want %q", tt.code, ins.Text, tt.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,12 @@ func Decode(a arch.Arch, code []byte, addr uint64) (Instruction, error) {
|
||||
}
|
||||
inst, err := arm64asm.Decode(code)
|
||||
if err != nil {
|
||||
// A few exception-space words the decoder refuses outright
|
||||
// (HVC, SMC, SB) live in the supplementary naming table;
|
||||
// anything else keeps the placeholder.
|
||||
if text, n, ok := nameARM64Rejected(code); ok {
|
||||
return Instruction{Addr: addr, Text: text, Len: n}, nil
|
||||
}
|
||||
return Instruction{Addr: addr, Text: "???", Len: 4}, nil
|
||||
}
|
||||
return Instruction{Addr: addr, Text: arm64asm.GoSyntax(inst, addr, nil, nil), Len: 4}, nil
|
||||
|
||||
Vendored
+3
@@ -142,3 +142,6 @@ e15a8b4d VST1.P V1.H[7], (R23)(R11)
|
||||
b786904d VST1.P V23.D[1], (R21)(R16)
|
||||
4d03631e UCVTFWD R26, F13
|
||||
2c3c0e0e VMOV V1.H[3], R12
|
||||
82fe1dd4 HVC $61428
|
||||
238b12d4 SMC $37977
|
||||
ff3003d5 SB
|
||||
Reference in new issue
Block a user