93 lines
2.4 KiB
Go
93 lines
2.4 KiB
Go
// Command asmcheck reads `go tool objdump` output on stdin, extracts every Go
|
|
// (Plan 9) instruction mnemonic, and reports which ones asmhelp fails to
|
|
// resolve to real reference data. Throwaway analysis tool.
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"loov.dev/lensm/internal/asmhelp"
|
|
)
|
|
|
|
func main() {
|
|
arch := "arm64"
|
|
if len(os.Args) > 1 {
|
|
arch = os.Args[1]
|
|
}
|
|
|
|
count := map[string]int{} // mnemonic -> occurrences
|
|
sample := map[string]string{} // mnemonic -> one full instruction line
|
|
|
|
sc := bufio.NewScanner(os.Stdin)
|
|
sc.Buffer(make([]byte, 1<<20), 1<<20)
|
|
for sc.Scan() {
|
|
line := sc.Text()
|
|
if strings.HasPrefix(strings.TrimSpace(line), "TEXT ") {
|
|
continue // function header pseudo-op, not an instruction
|
|
}
|
|
// objdump instruction lines are tab-separated (with empty padding
|
|
// fields): file:line, addr, hex, instr. The instruction is the last
|
|
// non-empty field.
|
|
var instr string
|
|
for _, f := range strings.Split(line, "\t") {
|
|
if f = strings.TrimSpace(f); f != "" {
|
|
instr = f
|
|
}
|
|
}
|
|
if !strings.HasPrefix(strings.TrimSpace(line), " ") && !strings.Contains(line, "0x") {
|
|
continue // function header line, not an instruction
|
|
}
|
|
toks := strings.Fields(instr)
|
|
if len(toks) == 0 {
|
|
continue
|
|
}
|
|
m := toks[0]
|
|
count[m]++
|
|
if sample[m] == "" {
|
|
sample[m] = instr
|
|
}
|
|
}
|
|
|
|
var unresolved, notPlausible []string
|
|
resolved := 0
|
|
for m := range count {
|
|
help, ok := asmhelp.ForInstruction(arch, "", sample[m])
|
|
switch {
|
|
case ok && help.Note == "":
|
|
resolved++
|
|
case ok && help.Note != "":
|
|
unresolved = append(unresolved, m)
|
|
default:
|
|
notPlausible = append(notPlausible, m)
|
|
}
|
|
}
|
|
|
|
byCountDesc := func(list []string) {
|
|
sort.Slice(list, func(i, j int) bool {
|
|
if count[list[i]] != count[list[j]] {
|
|
return count[list[i]] > count[list[j]]
|
|
}
|
|
return list[i] < list[j]
|
|
})
|
|
}
|
|
byCountDesc(unresolved)
|
|
byCountDesc(notPlausible)
|
|
|
|
fmt.Printf("unique mnemonics: %d resolved: %d unresolved: %d not-plausible: %d\n\n",
|
|
len(count), resolved, len(unresolved), len(notPlausible))
|
|
fmt.Println("UNRESOLVED (plausible mnemonics with no reference data), by frequency:")
|
|
for _, m := range unresolved {
|
|
fmt.Printf(" %6d %-12s %s\n", count[m], m, sample[m])
|
|
}
|
|
if len(notPlausible) > 0 {
|
|
fmt.Println("\nNOT-PLAUSIBLE (skipped: directives, undecodable, etc.):")
|
|
for _, m := range notPlausible {
|
|
fmt.Printf(" %6d %s\n", count[m], m)
|
|
}
|
|
}
|
|
}
|