Files
nexus/internal/leviathan/disasm/arm32.go
T
2026-06-16 11:51:07 +01:00

210 lines
4.8 KiB
Go

package disasm
import (
"encoding/binary"
"fmt"
"strings"
)
// diARM32 is a heuristic ARM state decoder for 32-bit little-endian ARM
// instructions. Thumb/T32 is intentionally not decoded here.
func diARM32(buf []byte, base int64, maxInstr int) []map[string]any {
out := []map[string]any{}
i := 0
for i+4 <= len(buf) && len(out) < maxInstr {
w := binary.LittleEndian.Uint32(buf[i : i+4])
mn, ops, conf := decodeARM32(w, base+int64(i))
out = append(out, map[string]any{
"offset": base + int64(i),
"bytes": hexStr(buf[i : i+4]),
"mnemonic": mn,
"operands": ops,
"confident": conf,
})
i += 4
}
for i < len(buf) && len(out) < maxInstr {
out = append(out, map[string]any{
"offset": base + int64(i),
"bytes": hexStr(buf[i : i+1]),
"mnemonic": "db",
"operands": hexCommaList(buf[i : i+1]),
"confident": false,
})
i++
}
return out
}
func decodeARM32(w uint32, pc int64) (string, string, bool) {
cond := (w >> 28) & 0xf
suf := armCondSuffix(cond)
if cond == 0xf {
suf = ".nv"
}
// BX Rn
if w&0x0ffffff0 == 0x012fff10 {
return "bx" + suf, armReg(w & 0xf), true
}
// SVC #imm24
if w&0x0f000000 == 0x0f000000 {
return "svc" + suf, fmt.Sprintf("#0x%x", w&0x00ffffff), true
}
// B / BL, offset is sign-extended imm24 << 2 from PC+8.
if w&0x0e000000 == 0x0a000000 {
imm := signExtend64(uint64(w&0x00ffffff)<<2, 26)
target := pc + 8 + int64(imm)
if w&0x01000000 != 0 {
return "bl" + suf, fmt.Sprintf("0x%x", target), true
}
return "b" + suf, fmt.Sprintf("0x%x", target), true
}
// PUSH/POP common aliases.
if w&0xffff0000 == 0xe92d0000 {
return "push", armRegList(w & 0xffff), true
}
if w&0xffff0000 == 0xe8bd0000 {
return "pop", armRegList(w & 0xffff), true
}
// LDR/STR immediate offset.
if w&0x0c000000 == 0x04000000 {
p := (w >> 24) & 1
u := (w >> 23) & 1
b := (w >> 22) & 1
wb := (w >> 21) & 1
l := (w >> 20) & 1
rn := (w >> 16) & 0xf
rd := (w >> 12) & 0xf
imm := w & 0xfff
mn := "str"
if l == 1 {
mn = "ldr"
}
if b == 1 {
mn += "b"
}
mn += suf
sign := ""
if u == 0 {
sign = "-"
}
addr := ""
switch {
case p == 1 && imm == 0 && wb == 0:
addr = fmt.Sprintf("[%s]", armReg(rn))
case p == 1:
addr = fmt.Sprintf("[%s, #%s0x%x]", armReg(rn), sign, imm)
if wb == 1 {
addr += "!"
}
default:
addr = fmt.Sprintf("[%s], #%s0x%x", armReg(rn), sign, imm)
}
return mn, fmt.Sprintf("%s, %s", armReg(rd), addr), true
}
// Data processing immediate/register subset.
if w&0x0c000000 == 0x00000000 || w&0x0e000000 == 0x02000000 {
immediate := w&0x02000000 != 0
opcode := (w >> 21) & 0xf
s := (w >> 20) & 1
rn := (w >> 16) & 0xf
rd := (w >> 12) & 0xf
mn := armDataProcName(opcode)
if mn == "" {
return ".word", fmt.Sprintf("0x%08x", w), false
}
mn += suf
if s == 1 && opcode != 8 && opcode != 9 && opcode != 10 && opcode != 11 {
mn += "s"
}
op2 := ""
if immediate {
imm8 := w & 0xff
rot := ((w >> 8) & 0xf) * 2
imm := (imm8 >> rot) | (imm8 << ((32 - rot) & 31))
op2 = fmt.Sprintf("#0x%x", imm)
} else {
rm := w & 0xf
shift := (w >> 4) & 0xff
if shift == 0 {
op2 = armReg(rm)
} else {
op2 = fmt.Sprintf("%s, %s", armReg(rm), armShift(shift))
}
}
switch opcode {
case 8, 9, 10, 11: // tst/teq/cmp/cmn
return mn, fmt.Sprintf("%s, %s", armReg(rn), op2), true
case 13, 15: // mov/mvn
return mn, fmt.Sprintf("%s, %s", armReg(rd), op2), true
default:
return mn, fmt.Sprintf("%s, %s, %s", armReg(rd), armReg(rn), op2), true
}
}
return ".word", fmt.Sprintf("0x%08x", w), false
}
func armCondSuffix(cond uint32) string {
return [...]string{".eq", ".ne", ".cs", ".cc", ".mi", ".pl", ".vs", ".vc", ".hi", ".ls", ".ge", ".lt", ".gt", ".le", "", ""}[cond&0xf]
}
func armDataProcName(op uint32) string {
return [...]string{"and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", "tst", "teq", "cmp", "cmn", "orr", "mov", "bic", "mvn"}[op&0xf]
}
func armReg(r uint32) string {
switch r & 0xf {
case 13:
return "sp"
case 14:
return "lr"
case 15:
return "pc"
default:
return fmt.Sprintf("r%d", r&0xf)
}
}
func armRegList(mask uint32) string {
regs := []string{}
start := -1
flush := func(end int) {
if start < 0 {
return
}
if start == end {
regs = append(regs, armReg(uint32(start)))
} else {
regs = append(regs, fmt.Sprintf("%s-%s", armReg(uint32(start)), armReg(uint32(end))))
}
start = -1
}
for i := 0; i < 16; i++ {
if mask&(1<<uint(i)) != 0 {
if start < 0 {
start = i
}
continue
}
flush(i - 1)
}
flush(15)
return "{" + strings.Join(regs, ", ") + "}"
}
func armShift(v uint32) string {
typ := (v >> 1) & 0x3
imm := (v >> 3) & 0x1f
names := [...]string{"lsl", "lsr", "asr", "ror"}
if imm == 0 {
if typ == 0 {
return "lsl #0"
}
if typ == 3 {
return "rrx"
}
imm = 32
}
return fmt.Sprintf("%s #%d", names[typ], imm)
}