fix(cmd): usage exit codes, asm output file and cross-arch ground truth
Assisted-by: GLM 5.3
This commit is contained in:
+76
-51
@@ -10,6 +10,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -48,6 +49,22 @@ func version() string {
|
||||
return bi.Main.Version
|
||||
}
|
||||
|
||||
// usageError marks an error the caller's arguments caused, which exits 2
|
||||
// instead of the 1 a runtime failure gets.
|
||||
type usageError struct{ err error }
|
||||
|
||||
func (e *usageError) Error() string { return e.err.Error() }
|
||||
func (e *usageError) Unwrap() error { return e.err }
|
||||
|
||||
// exitCodeFor maps an error onto the process exit status: 2 for a usage
|
||||
// error, 1 for anything else.
|
||||
func exitCodeFor(err error) int {
|
||||
if _, ok := errors.AsType[*usageError](err); ok {
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
usage(os.Stderr)
|
||||
@@ -77,12 +94,12 @@ func main() {
|
||||
case "audit-instructions":
|
||||
if err := cmdAuditInstructions(os.Args[2:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
os.Exit(exitCodeFor(err))
|
||||
}
|
||||
case "scaffold":
|
||||
if err := cmdScaffold(os.Args[2:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
os.Exit(exitCodeFor(err))
|
||||
}
|
||||
case "lsp":
|
||||
os.Exit(cmdLSP(os.Args[2:]))
|
||||
@@ -104,11 +121,11 @@ func cmdVersion() int {
|
||||
|
||||
// ANSI colour helpers for terminal output.
|
||||
const (
|
||||
colorReset = "\033[0m"
|
||||
colorBold = "\033[1m"
|
||||
colorCyan = "\033[36m"
|
||||
colorYellow = "\033[33m"
|
||||
colorGray = "\033[90m"
|
||||
colourReset = "\033[0m"
|
||||
colourBold = "\033[1m"
|
||||
colourCyan = "\033[36m"
|
||||
colourYellow = "\033[33m"
|
||||
colourGrey = "\033[90m"
|
||||
)
|
||||
|
||||
// isTTY reports whether the writer is a terminal (for colour output).
|
||||
@@ -122,9 +139,9 @@ func isTTY(w io.Writer) bool {
|
||||
|
||||
func usage(w io.Writer) {
|
||||
useColor := isTTY(w)
|
||||
bold, cyan, yellow, gray, reset := "", "", "", "", ""
|
||||
bold, cyan, yellow, grey, reset := "", "", "", "", ""
|
||||
if useColor {
|
||||
bold, cyan, yellow, gray, reset = colorBold, colorCyan, colorYellow, colorGray, colorReset
|
||||
bold, cyan, yellow, grey, reset = colourBold, colourCyan, colourYellow, colourGrey, colourReset
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%sgasm %s%s: developer tooling for Go's Plan 9 assembler (GAsm)%s\n\n", bold, version(), reset, reset)
|
||||
@@ -153,12 +170,12 @@ func usage(w io.Writer) {
|
||||
{"version", "print the version (same as --version)"},
|
||||
}
|
||||
for _, c := range commands {
|
||||
fmt.Fprintf(w, " %s%-10s%s %s%s%s\n", cyan, c.name, reset, gray, c.desc, reset)
|
||||
fmt.Fprintf(w, " %s%-10s%s %s%s%s\n", cyan, c.name, reset, grey, c.desc, reset)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "\n%sFlags:%s\n", yellow, reset)
|
||||
fmt.Fprintf(w, " %s-h, --help%s %sshow this help%s\n", cyan, reset, gray, reset)
|
||||
fmt.Fprintf(w, " %s-V, --version%s %sprint the version%s\n", cyan, reset, gray, reset)
|
||||
fmt.Fprintf(w, " %s-h, --help%s %sshow this help%s\n", cyan, reset, grey, reset)
|
||||
fmt.Fprintf(w, " %s-V, --version%s %sprint the version%s\n", cyan, reset, grey, reset)
|
||||
|
||||
fmt.Fprintf(w, "\nRun \"gasm <command> -h\" for a command's usage and flags.\n\n")
|
||||
|
||||
@@ -172,7 +189,7 @@ func usage(w io.Writer) {
|
||||
}
|
||||
for _, e := range examples {
|
||||
if e.desc != "" {
|
||||
fmt.Fprintf(w, " %s%s%s %s%s%s\n", cyan, e.cmd, reset, gray, e.desc, reset)
|
||||
fmt.Fprintf(w, " %s%s%s %s%s%s\n", cyan, e.cmd, reset, grey, e.desc, reset)
|
||||
} else {
|
||||
fmt.Fprintf(w, " %s%s%s\n", cyan, e.cmd, reset)
|
||||
}
|
||||
@@ -483,6 +500,14 @@ requires -p, the package path, and the installed Go toolchain).
|
||||
fmt.Fprintln(os.Stderr, "usage: gasm asm [--format raw|elf|goobj] [-p pkg] [-GOARCH arch] [-o out] <file>")
|
||||
return 2
|
||||
}
|
||||
// The format is validated before anything else, so a bogus value exits 2
|
||||
// with or without -o instead of silently dumping the hex of a raw image.
|
||||
switch *format {
|
||||
case "raw", "elf", "goobj":
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "gasm asm: unknown format %q (want raw, elf or goobj)\n", *format)
|
||||
return 2
|
||||
}
|
||||
path := fs.Arg(0)
|
||||
targetArch := arch.FromFilename(path)
|
||||
if *archName != "" {
|
||||
@@ -517,38 +542,42 @@ requires -p, the package path, and the installed Go toolchain).
|
||||
fmt.Fprintln(os.Stderr, "gasm asm: no assemblable TEXT functions or GLOBL data found")
|
||||
return 1
|
||||
}
|
||||
for _, fn := range img.Funcs {
|
||||
code := img.Code[fn.Offset : fn.Offset+fn.Size]
|
||||
fmt.Printf("%s: %d bytes\n", fn.Name, fn.Size)
|
||||
for i := 0; i < len(code); i += 16 {
|
||||
end := min(i+16, len(code))
|
||||
fmt.Printf(" %04x:", i)
|
||||
for _, b := range code[i:end] {
|
||||
fmt.Printf(" %02x", b)
|
||||
// Without -o the hex dump on stdout is the output; with -o the file is,
|
||||
// and the dump is skipped, as the -o help text promises.
|
||||
if *out == "" {
|
||||
for _, fn := range img.Funcs {
|
||||
code := img.Code[fn.Offset : fn.Offset+fn.Size]
|
||||
fmt.Printf("%s: %d bytes\n", fn.Name, fn.Size)
|
||||
for i := 0; i < len(code); i += 16 {
|
||||
end := min(i+16, len(code))
|
||||
fmt.Printf(" %04x:", i)
|
||||
for _, b := range code[i:end] {
|
||||
fmt.Printf(" %02x", b)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
if len(img.Data) > 0 {
|
||||
fmt.Printf("data: %d bytes at 0x%x\n", len(img.Data), len(img.Code))
|
||||
for _, d := range f.Decls {
|
||||
g, ok := d.(*ast.Globl)
|
||||
if !ok || g.Name == nil || g.Name.Pseudo != "SB" {
|
||||
continue
|
||||
if len(img.Data) > 0 {
|
||||
fmt.Printf("data: %d bytes at 0x%x\n", len(img.Data), len(img.Code))
|
||||
for _, d := range f.Decls {
|
||||
g, ok := d.(*ast.Globl)
|
||||
if !ok || g.Name == nil || g.Name.Pseudo != "SB" {
|
||||
continue
|
||||
}
|
||||
size := 0
|
||||
if g.Size != nil && g.Size.Imm.HasVal {
|
||||
size = int(g.Size.Imm.Val)
|
||||
}
|
||||
fmt.Printf(" %s: %d bytes at 0x%x\n", g.Name.Name, size, img.Symbols[g.Name.Name])
|
||||
}
|
||||
size := 0
|
||||
if g.Size != nil && g.Size.Imm.HasVal {
|
||||
size = int(g.Size.Imm.Val)
|
||||
for i := 0; i < len(img.Data); i += 16 {
|
||||
end := min(i+16, len(img.Data))
|
||||
fmt.Printf(" %04x:", len(img.Code)+i)
|
||||
for _, b := range img.Data[i:end] {
|
||||
fmt.Printf(" %02x", b)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
fmt.Printf(" %s: %d bytes at 0x%x\n", g.Name.Name, size, img.Symbols[g.Name.Name])
|
||||
}
|
||||
for i := 0; i < len(img.Data); i += 16 {
|
||||
end := min(i+16, len(img.Data))
|
||||
fmt.Printf(" %04x:", len(img.Code)+i)
|
||||
for _, b := range img.Data[i:end] {
|
||||
fmt.Printf(" %02x", b)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
if *out != "" {
|
||||
@@ -586,9 +615,6 @@ requires -p, the package path, and the installed Go toolchain).
|
||||
obj, err = img.GOObject(*pkg, path)
|
||||
}
|
||||
kind = "Go object"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "gasm asm: unknown format %q (want raw, elf or goobj)\n", *format)
|
||||
return 2
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "gasm asm:", err)
|
||||
@@ -1012,15 +1038,14 @@ each entry reproduces.
|
||||
// under the available loong64 emulators), so those kernels take the
|
||||
// toolchain-comparison path.
|
||||
if targetArch != hostArch() || targetArch == arch.LOONG64 {
|
||||
// No JIT on this host: ground truth and profile remain available.
|
||||
// (loong64 is ground-truth-only everywhere for now: its trampoline
|
||||
// is implemented but not yet validated against real hardware.)
|
||||
// No JIT on this host: ground truth and profile remain available for
|
||||
// every architecture, because cmdVerifyNonJIT assembles and compares
|
||||
// against the toolchain without executing anything. (loong64 is
|
||||
// ground-truth-only everywhere for now: its trampoline is implemented
|
||||
// but not yet validated against real hardware.)
|
||||
switch targetArch {
|
||||
case arch.RISCV, arch.LOONG64, arch.ARM64:
|
||||
case arch.AMD64, arch.RISCV, arch.LOONG64, arch.ARM64:
|
||||
return cmdVerifyNonJIT(path, targetArch, *groundTruth, *profile)
|
||||
case arch.AMD64:
|
||||
fmt.Fprintln(os.Stderr, "gasm verify: JIT-based checks need an amd64 host; use --ground-truth here")
|
||||
return 1
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "gasm verify: unsupported architecture")
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user