fix: staticcheck and deadcode findings repo-wide, modernize counting loops

This commit is contained in:
2026-08-29 15:25:15 +02:00
parent 9a34733615
commit eacf33d0f7
33 changed files with 88 additions and 250 deletions
+1 -4
View File
@@ -109,16 +109,13 @@ func main() {
if err != nil {
t.Fatalf("baseline build: %v\n%s", err, buildLog)
}
var pkgArch, work, linkLine, asmObj string
var work, linkLine, asmObj string
for _, line := range strings.Split(string(buildLog), "\n") {
switch {
case strings.HasPrefix(line, "WORK="):
work = strings.TrimPrefix(line, "WORK=")
case strings.Contains(line, "/asm ") && strings.Contains(line, "main_arm64.s") && !strings.Contains(line, "-gensymabis"):
asmObj = fieldAfter(line, "-o")
case strings.Contains(line, "pack r") && strings.Contains(line, "_pkg_.a"):
pkgArch = strings.TrimSpace(strings.SplitN(line, "pack r", 2)[1])
pkgArch = strings.Fields(strings.SplitN(pkgArch, "#", 2)[0])[0]
case strings.Contains(line, "/link ") && strings.Contains(line, "-importcfg"):
linkLine = line
}
-5
View File
@@ -878,11 +878,6 @@ func encodeARM64SBStore(sym *ast.Symbol, rs int, mnem string, relocs *[]Reloc) (
// ---- operand helpers ----
// arm64Reg returns the register number of an operand, or -1.
func arm64Reg(op *ast.Operand) int {
return arm64RegNum(operandRegName(op))
}
// arm64Imm64 returns the full 64-bit immediate value of an operand.
func arm64Imm64(op *ast.Operand) int64 {
if op.Imm.HasVal {
-75
View File
@@ -115,12 +115,6 @@ func arm64RegNum(name string) int {
return -1
}
// arm64IsSP reports whether a register operand is the stack pointer (R31/SP),
// which uses a different encoding path for some instructions.
func arm64IsSP(name string) bool {
return name == "SP"
}
// ---- format helpers ----
// a64wordLE encodes a uint32 as 4 little-endian bytes.
@@ -137,14 +131,6 @@ func a64WordsLE(ws ...uint32) []byte {
return out
}
// ---- data-processing (shifted register) ----
// a64DPSR encodes a data-processing (shifted register) instruction:
// sf<<31 | op<<30 | S<<29 | 0x0b<<24 | shift<<22 | 0<<21 | Rm<<16 | imm6<<10 | Rn<<5 | Rd.
func a64DPSR(sf, op, S, shift, rm, imm6, rn, rd uint32) uint32 {
return sf<<31 | op<<30 | S<<29 | 0x0b<<24 | shift<<22 | rm<<16 | imm6<<10 | rn<<5 | rd
}
// ---- data-processing (immediate) ----
// a64AddSub encodes an ADD/SUB (immediate) instruction:
@@ -153,14 +139,6 @@ func a64AddSub(sf, op, S, sh, imm12, rn, rd uint32) uint32 {
return sf<<31 | op<<30 | S<<29 | 0x11<<24 | sh<<22 | imm12<<10 | rn<<5 | rd
}
// ---- logical (immediate) ----
// a64LogicalImm encodes a logical (immediate) instruction:
// sf<<31 | opc<<29 | 0x24<<23 | N<<22 | immr<<16 | imms<<10 | Rn<<5 | Rd.
func a64LogicalImm(sf, opc, N, immr, imms, rn, rd uint32) uint32 {
return sf<<31 | opc<<29 | 0x24<<23 | N<<22 | immr<<16 | imms<<10 | rn<<5 | rd
}
// ---- move wide ----
// a64MoveWide encodes a MOVZ/MOVK/MOVN instruction:
@@ -198,38 +176,6 @@ func a64LSP(opc, V, L uint32, imm7 int32, rt2, rn, rt uint32) uint32 {
return opc<<30 | 5<<27 | V<<26 | 2<<23 | L<<22 | (uint32(imm7)&0x7F)<<15 | rt2<<10 | rn<<5 | rt
}
// ---- load/store pair (pre-index) ----
// a64LSPPre encodes a load/store pair (pre-index):
// opc<<30 | 0x5<<27 | V<<26 | 0b11<<23 | L<<22 | imm7<<15 | Rt2<<10 | Rn<<5 | Rt.
func a64LSPPre(opc, V, L uint32, imm7 int32, rt2, rn, rt uint32) uint32 {
return opc<<30 | 5<<27 | V<<26 | 3<<23 | L<<22 | (uint32(imm7)&0x7F)<<15 | rt2<<10 | rn<<5 | rt
}
// ---- load/store pair (post-index) ----
// a64LSPPost encodes a load/store pair (post-index):
// opc<<30 | 0x5<<27 | V<<26 | 0b01<<23 | L<<22 | imm7<<15 | Rt2<<10 | Rn<<5 | Rt.
func a64LSPPost(opc, V, L uint32, imm7 int32, rt2, rn, rt uint32) uint32 {
return opc<<30 | 5<<27 | V<<26 | 1<<23 | L<<22 | (uint32(imm7)&0x7F)<<15 | rt2<<10 | rn<<5 | rt
}
// ---- pre-index load/store ----
// a64LSPreIndex encodes a load/store register (pre-index):
// size<<30 | 0x7<<27 | V<<26 | opc<<22 | 1<<11 | 1<<10 | imm9<<12 | Rn<<5 | Rt.
func a64LSPreIndex(size, V, opc uint32, imm9 int32, rn, rt uint32) uint32 {
return size<<30 | 7<<27 | V<<26 | opc<<22 | 3<<10 | (uint32(imm9)&0x1FF)<<12 | rn<<5 | rt
}
// ---- post-index load/store ----
// a64LSPostIndex encodes a load/store register (post-index):
// size<<30 | 0x7<<27 | V<<26 | opc<<22 | 0<<11 | 1<<10 | imm9<<12 | Rn<<5 | Rt.
func a64LSPostIndex(size, V, opc uint32, imm9 int32, rn, rt uint32) uint32 {
return size<<30 | 7<<27 | V<<26 | opc<<22 | 1<<10 | (uint32(imm9)&0x1FF)<<12 | rn<<5 | rt
}
// ---- branches ----
// a64Branch encodes an unconditional branch (B/BL):
@@ -259,14 +205,6 @@ func a64ADR(p uint32, immhi int32, immlo uint32, rd uint32) uint32 {
return p<<31 | immlo<<29 | 0x10<<24 | (uint32(immhi)&0x7FFFF)<<5 | rd
}
// ---- EXTR ----
// a64EXTR encodes an EXTR instruction:
// sf<<31 | 0<<29 | 0x27<<23 | N<<22 | 0<<21 | Rm<<16 | imms<<10 | Rn<<5 | Rd.
func a64EXTR(sf, N, rm, imms, rn, rd uint32) uint32 {
return sf<<31 | 0x27<<23 | N<<22 | rm<<16 | imms<<10 | rn<<5 | rd
}
// ---- system ----
// a64NOP encodes a NOP: 0xd503201f.
@@ -359,7 +297,6 @@ const (
type a64Enc struct {
format a64Format
op uint32 // the pre-positioned opcode bits
size int // 4 for most, 8 for DP-imm with shift, etc.
}
// a64InstrTable maps AArch64 mnemonics (as the Go assembler spells them) to
@@ -717,18 +654,6 @@ func a64StoreOpc(t a64LSType) int {
return 0 // integer store
}
// a64MovRegTable maps register-to-register MOV mnemonic expansions.
// The Go toolchain encodes MOV Rn, Rd as ORR Rn, ZR, Rd.
var a64MovRegTable = map[string]uint32{
"MOVD": 1<<31 | 1<<29 | 0x0a<<24, // ORR 64-bit
"MOVW": 0<<31 | 1<<29 | 0x0a<<24, // ORR 32-bit
"MOVB": 0<<31 | 1<<29 | 0x0a<<24, // ORR 32-bit (byte move)
"MOVBU": 0<<31 | 1<<29 | 0x0a<<24, // ORR 32-bit
"MOVH": 0<<31 | 1<<29 | 0x0a<<24, // ORR 32-bit
"MOVHU": 0<<31 | 1<<29 | 0x0a<<24, // ORR 32-bit
"MOVWU": 0<<31 | 1<<29 | 0x0a<<24, // ORR 32-bit
}
// arm64RegClass discriminates integer (R), floating-point (F) registers for
// the MOV pseudo-instruction.
type arm64RegClass int
-2
View File
@@ -45,8 +45,6 @@ const (
sttSection = 3
stInfoShift = 4
shnUndef = 0
rX8664PC32 = 2
rX8664_64 = 1 // R_X86_64_64 (absolute 64-bit)
)
-24
View File
@@ -3,8 +3,6 @@
package asm
import "encoding/binary"
// dwarfELFSections holds the laid-out DWARF sections ready for inclusion
// in an ELF file.
type dwarfELFSections struct {
@@ -114,28 +112,6 @@ func appendDWARFSections(out *[]byte, img *Image, srcFile string, symIdx map[str
return result
}
// dwarfRelaRecords returns the ELF RELA records for DWARF address fixups.
// Each record uses R_X86_64_64 (absolute 64-bit) relocation type.
func dwarfRelaRecords(dw *dwarfELFSections, relType uint32) []byte {
le := binary.LittleEndian
var out []byte
for _, r := range dw.infoRelocs {
var b [24]byte
le.PutUint64(b[0:], r.off)
le.PutUint64(b[8:], uint64(r.sym)<<32|uint64(relType))
le.PutUint64(b[16:], uint64(r.addend))
out = append(out, b[:]...)
}
for _, r := range dw.lineRelocs {
var b [24]byte
le.PutUint64(b[0:], r.off)
le.PutUint64(b[8:], uint64(r.sym)<<32|uint64(relType))
le.PutUint64(b[16:], uint64(r.addend))
out = append(out, b[:]...)
}
return out
}
// dwarfSectionNames returns the DWARF section names for the string table.
var dwarfSectionNames = []string{
".debug_abbrev", ".debug_info", ".debug_line", ".debug_line_str",
+1 -1
View File
@@ -505,7 +505,7 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
// The fingerprint stays zero, as cmd/asm leaves it.
binary.LittleEndian.PutUint32(payload[16:], 4) // ObjFlagFromAssembly
off := uint32(headerSize + len(strTab))
for i := 0; i < blkEnd; i++ {
for i := range blkEnd {
binary.LittleEndian.PutUint32(payload[20+4*i:], off)
off += uint32(len(blocks[i]))
}
+1 -19
View File
@@ -12,24 +12,6 @@ import (
"strings"
)
// readGOOBJSymbols reads the GOOBJ symbol definitions from a compiled Go
// package's export file. The file is an ar archive containing a __.PKGDEF
// member whose payload is the "go object ...\n!\n" preamble followed by the
// GOOBJ data. The function returns the symbol names in definition order
// (the order they appear in blkSymdef), which matches the SymIdx the linker
// expects for cross-package references.
func readGOOBJSymbols(exportPath string) ([]string, error) {
data, err := os.ReadFile(exportPath)
if err != nil {
return nil, err
}
goobj, err := extractGOOBJ(data)
if err != nil {
return nil, fmt.Errorf("%s: %w", exportPath, err)
}
return goobj.symbols(), nil
}
// exportPath returns the export file path for a given import path by running
// "go list -export". The result is cached so repeated calls for the same
// package are fast.
@@ -221,7 +203,7 @@ func (f *goobjFile) readSymNames(block []byte) []string {
}
n := len(block) / recSize
names := make([]string, 0, n)
for i := 0; i < n; i++ {
for i := range n {
rec := block[i*recSize : (i+1)*recSize]
nameLen := binary.LittleEndian.Uint32(rec[0:4])
nameOff := binary.LittleEndian.Uint32(rec[4:8])
-5
View File
@@ -1342,11 +1342,6 @@ func l64Reg(op *ast.Operand) int {
return loong64RegNum(operandRegName(op))
}
// l64Imm returns the immediate value of an operand.
func l64Imm(op *ast.Operand) int32 {
return immFromOperand(op)
}
// l64Imm64 returns the full 64-bit immediate value of an operand.
func l64Imm64(op *ast.Operand) int64 {
if op.Imm.HasVal {
-5
View File
@@ -37,11 +37,6 @@ func Idx(base, index Reg, scale int, disp int64, size int) Mem {
return Mem{Base: base, Index: index, Scale: scale, Disp: disp, Size: size, HasBase: true, HasIndex: true}
}
// Rip builds a RIP-relative memory operand (RIP)+disp.
func Rip(disp int64, size int) Mem {
return Mem{Disp: disp, Size: size}
}
// sbMem is a memory operand that references a static (SB) symbol. It encodes
// as a RIP-relative reference with a placeholder displacement; the encoder
// records a patch site so the file-level layout can fill in the true rel32
-5
View File
@@ -1250,11 +1250,6 @@ func isFPCmpInstr(m string) bool {
return false
}
func isFPCvtInstr(m string) bool {
_, ok := riscvCvtTable[m]
return ok
}
// Operand helpers.
func regFromOperand(op *ast.Operand) int {
// Register is in Addr.Base (from (base) syntax) or Addr.Sym.Name (bare ident).
+3 -11
View File
@@ -66,7 +66,7 @@ mnemonic, so the audit tracks whatever toolchain `+"`go env GOROOT`"+` names.
return err
}
var superset, backlog, shared, goOnly []string
var superset, backlog, shared []string
for _, name := range names {
switch {
case !asm.Encodable(name):
@@ -77,16 +77,8 @@ mnemonic, so the audit tracks whatever toolchain `+"`go env GOROOT`"+` names.
shared = append(shared, name)
}
}
for _, name := range names {
if goKnown[name] && !seen[name] {
goOnly = append(goOnly, name)
}
}
// GO-ONLY is computed from the complement of the gasm table, which this
// audit cannot enumerate (Go's table is only visible through probes of
// names we already know). Report it as informational zero instead of a
// misleading list.
goOnly = nil
// GO-ONLY is not enumerable by probing: Go's table is only visible
// through names we already know, so nothing can be reported there.
sort.Strings(superset)
sort.Strings(backlog)
+2 -2
View File
@@ -1526,7 +1526,7 @@ func cmdVerifyCall(k *verify.Kernel, path, funcName, bufSpec, scalarSpec string,
fmt.Printf(" args before: %s\n", hexDump(args))
rc := 0
for i := 0; i < repeat; i++ {
for i := range repeat {
out, err := k.CallFunc(funcName, args)
if err != nil {
fmt.Printf(" call %d: FAIL — %v\n", i+1, err)
@@ -1575,7 +1575,7 @@ func hexDump(buf []byte) string {
n = max
}
var sb strings.Builder
for i := 0; i < n; i++ {
for i := range n {
if i > 0 {
sb.WriteByte(' ')
}
+16 -5
View File
@@ -60,6 +60,7 @@ bodies, place the file in the kernel's package, and run it in CI.
out.WriteString(headerComment)
out.WriteString("package " + packageName + "\n\n")
out.WriteString("import (\n\t\"bytes\"\n\t\"math/rand\"\n\t\"testing\"\n)\n\n")
out.WriteString(generatedHelpers)
kernels := 0
for _, d := range f.Decls {
@@ -305,9 +306,12 @@ func goCast(elem string) string {
}
}
// outputBytes narrows a returned slice to bytes for the comparison; scalar
// results are compared through the same helper via a reflect-free trick the
// author may need to adjust for non-slice returns.
// generatedHelpers is emitted into every generated test file: outputBytes
// narrows returned slices and scalars to a byte form for the comparison.
// It lives in the template, not in this binary, because only the generated
// file ever calls it.
const generatedHelpers = `// outputBytes narrows a returned slice or scalar to bytes for the
// comparison; extend the switch when a kernel returns a wider type.
func outputBytes(v any) []byte {
switch t := v.(type) {
case []byte:
@@ -324,11 +328,18 @@ func outputBytes(v any) []byte {
case []uint16:
b := make([]byte, 2*len(t))
for i, x := range t {
b[i*4/2] = byte(x)
b[i*4/2+1] = byte(x >> 8)
b[i*2] = byte(x)
b[i*2+1] = byte(x >> 8)
}
return b
case int:
b := make([]byte, 8)
for i := 0; i < 8; i++ {
b[i] = byte(uint64(t) >> (8 * i))
}
return b
default:
return nil
}
}
`
-5
View File
@@ -114,11 +114,6 @@ func (bm *Breakpoints) SetWithCond(addr uint64, label string, cond *Condition) (
return bp, nil
}
// Hits returns the number of times the breakpoint has been hit.
func (bp *Breakpoint) Hits() int {
return bp.hits
}
// Info returns a formatted list of all breakpoints.
func (bm *Breakpoints) Info() string {
if len(bm.bps) == 0 {
+1 -1
View File
@@ -36,7 +36,7 @@ func (s *Session) Disassemble(addr uint64) (string, int, error) {
func (s *Session) DisassembleN(addr uint64, n int) string {
var result string
pc := addr
for i := 0; i < n; i++ {
for range n {
text, length, err := s.Disassemble(pc)
if err != nil {
result += fmt.Sprintf(" %#08x: <error: %v>\n", pc, err)
+1 -1
View File
@@ -30,7 +30,7 @@ func (s *Session) Disassemble(addr uint64) (string, int, error) {
func (s *Session) DisassembleN(addr uint64, n int) string {
var result string
pc := addr
for i := 0; i < n; i++ {
for i := range n {
text, length, err := s.Disassemble(pc)
if err != nil {
result += fmt.Sprintf(" %#08x: <error: %v>\n", pc, err)
+1 -1
View File
@@ -30,7 +30,7 @@ func (s *Session) Disassemble(addr uint64) (string, int, error) {
func (s *Session) DisassembleN(addr uint64, n int) string {
var result string
pc := addr
for i := 0; i < n; i++ {
for i := range n {
text, length, err := s.Disassemble(pc)
if err != nil {
result += fmt.Sprintf(" %#08x: <error: %v>\n", pc, err)
+1 -1
View File
@@ -30,7 +30,7 @@ func (s *Session) Disassemble(addr uint64) (string, int, error) {
func (s *Session) DisassembleN(addr uint64, n int) string {
var result string
pc := addr
for i := 0; i < n; i++ {
for i := range n {
text, length, err := s.Disassemble(pc)
if err != nil {
result += fmt.Sprintf(" %#08x: <error: %v>\n", pc, err)
+1 -1
View File
@@ -72,7 +72,7 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
if len(parts) > 1 {
n, _ = strconv.Atoi(parts[1])
}
for i := 0; i < n; i++ {
for range n {
if s.Exited() {
fmt.Println("debuggee exited")
break
+3 -3
View File
@@ -98,10 +98,10 @@ func RunTarget(asmPath, funcName, argsFile, tmpDir string) error {
stackArgs := make([]byte, fl.Args)
copy(stackArgs, args)
_, callErr := verify.Call(fnAddr, stackArgs)
if callErr != nil {
if _, callErr := verify.Call(fnAddr, stackArgs); callErr != nil {
os.Exit(1)
}
os.Exit(0)
// Success returns to the caller, which exits with status 0; the JIT
// code has already run to its own trampoline by the time Call returns.
return nil
}
+1 -1
View File
@@ -44,7 +44,7 @@ const (
)
func (s *Session) FindFreeWatchpointSlot() int {
for i := 0; i < maxWatchpoints; i++ {
for i := range maxWatchpoints {
if !wpSlots[i] {
return i
}
+1 -1
View File
@@ -27,7 +27,7 @@ var wpSlots [4]bool
const maxWatchpoints = 4
func (s *Session) FindFreeWatchpointSlot() int {
for i := 0; i < maxWatchpoints; i++ {
for i := range maxWatchpoints {
if !wpSlots[i] {
return i
}
+1 -1
View File
@@ -27,7 +27,7 @@ var wpSlots [4]bool
const maxWatchpoints = 4
func (s *Session) FindFreeWatchpointSlot() int {
for i := 0; i < maxWatchpoints; i++ {
for i := range maxWatchpoints {
if !wpSlots[i] {
return i
}
+1 -1
View File
@@ -119,7 +119,7 @@ func fieldsSizeAlign(list *ast.FieldList) (size, align int, ok bool) {
if n == 0 {
n = 1
}
for i := 0; i < n; i++ {
for range n {
offset = alignUp(offset, ea)
offset += es
}
+7 -2
View File
@@ -5,8 +5,9 @@ package lint
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"sourcedock.dev/petrbalvin/gasm-devkit/arch"
@@ -24,7 +25,11 @@ import (
// assembler accepts. Advisory warnings are reported but not fatal, since they
// are heuristics that may legitimately differ across Go versions.
func TestGoRuntimeCorpus(t *testing.T) {
dir := filepath.Join(runtime.GOROOT(), "src", "runtime")
out, err := exec.Command("go", "env", "GOROOT").Output()
if err != nil {
t.Skipf("cannot locate GOROOT: %v", err)
}
dir := filepath.Join(strings.TrimSpace(string(out)), "src", "runtime")
var files []string
for _, suffix := range []string{"_amd64.s", "_arm64.s", "_riscv64.s", "_loong64.s"} {
matches, _ := filepath.Glob(filepath.Join(dir, "*"+suffix))
-4
View File
@@ -295,10 +295,6 @@ type semanticTokensParams struct {
// --- initialize -------------------------------------------------------------
type initializeParams struct {
RootURI string `json:"rootUri"`
}
// ServerCapabilities advertises what this server provides.
type ServerCapabilities struct {
TextDocumentSync int `json:"textDocumentSync"`
+6 -9
View File
@@ -210,18 +210,17 @@ func (p *state) parseText(line []token.Token) {
rest = rest[1:]
}
// Frame: $number ; optional args: -number.
// Frame: $number ; optional args: -number. Whatever remains after the
// header is the body and is parsed by the caller.
if len(rest) > 0 && rest[0].Kind == token.Dollar {
text.Frame = parseOperand(rest[:2]) // "$" "number"
rest = rest[2:]
if len(rest) >= 2 && rest[0].Kind == token.Minus && rest[1].Kind == token.Number {
if len(rest) >= 4 && rest[2].Kind == token.Minus && rest[3].Kind == token.Number {
text.Args = &ast.Operand{
Kind: ast.OpImmediate,
Imm: ast.Immediate{Val: parseInt(rest[1].Text), HasVal: true},
Raw: "-" + rest[1].Text,
Pos: rest[0].Pos,
Imm: ast.Immediate{Val: parseInt(rest[3].Text), HasVal: true},
Raw: "-" + rest[3].Text,
Pos: rest[2].Pos,
}
rest = rest[2:]
}
}
@@ -387,10 +386,8 @@ func parseImmediate(g []token.Token) ast.Immediate {
} else {
imm.Float = text
}
i++
} else if i < len(g) && (g[i].Kind == token.String || g[i].Kind == token.Rune) {
imm.Str = g[i].Text
i++
}
return imm
}
+16 -5
View File
@@ -17,7 +17,12 @@ import (
var abiResult uint64
// savedBP holds the caller's frame pointer across the ABI-checked JIT call.
// Referenced by enterJITChecked to satisfy go vet's save-before-clobber rule.
// Written and read by enterJITChecked/leaveJITChecked (abi_amd64.s); no Go
// code references it, which GoLand cannot see inside assembly.
//
// noinspection GoUnusedGlobalVariable
//
//lint:ignore U1000 written and read by the assembly
var savedBP uintptr
// leaveCheckedPtr is initialised by the linker from the GLOBL/DATA in
@@ -26,7 +31,10 @@ var savedBP uintptr
var leaveCheckedPtr uintptr
// enterJITChecked sets sentinels in BP and R14, switches to the prepared
// stack and jumps to fn.
// stack and jumps to fn. The body lives in abi_amd64.s and reads the
// parameters from the frame by name, which GoLand cannot see.
//
// noinspection GoUnusedParameter
//
//go:nosplit
func enterJITChecked(fn uintptr, stack uintptr)
@@ -37,6 +45,9 @@ func enterJITChecked(fn uintptr, stack uintptr)
// declaration would generate. The declaration exists solely to satisfy
// go vet's "missing Go declaration" check.
//
// noinspection GoUnusedFunction
//
//lint:ignore U1000 the assembly obtains this address through leaveCheckedPtr
//go:nosplit
func leaveJITCheckedRaw()
@@ -95,10 +106,10 @@ func CallChecked(fnAddr uintptr, args []byte) ([]byte, ABIReport, error) {
if err != nil {
return nil, report, fmt.Errorf("verify: stack mmap: %w", err)
}
defer syscall.Munmap(stackMem)
defer func() { _ = syscall.Munmap(stackMem) }()
// Fill the red zone with the canary pattern.
for i := 0; i < redZoneSize; i++ {
for i := range redZoneSize {
stackMem[i] = redZoneFill
}
@@ -116,7 +127,7 @@ func CallChecked(fnAddr uintptr, args []byte) ([]byte, ABIReport, error) {
report.R14Clobbered = res&2 != 0
// Check the red zone.
for i := 0; i < redZoneSize; i++ {
for i := range redZoneSize {
if stackMem[i] != redZoneFill {
report.RedZoneHit = true
break
+6 -1
View File
@@ -14,7 +14,12 @@ import (
)
// savedSP holds the Go stack pointer while a JIT call is in flight.
// Referenced by the assembly trampoline (trampoline_amd64.s).
// Written and read by the assembly trampoline (trampoline_amd64.s); no Go
// code references it, which staticcheck and GoLand cannot see.
//
// noinspection GoUnusedGlobalVariable
//
//lint:ignore U1000 written and read by the assembly
var savedSP uintptr
// enterJIT switches to the prepared stack and jumps to fn.
-46
View File
@@ -55,49 +55,3 @@ func (k *Kernel) BlockCount(name string) (int, error) {
}
return len(blocks), nil
}
// PathFingerprint is the observable output of one function execution: the
// values written back into the result slots of the argument block. Two
// executions that produce the same fingerprint took observationally
// equivalent paths (though they may differ internally).
type PathFingerprint struct {
Results []uint64 // the result words from the arg block
}
// ProfilePaths runs the function with each of the given argument blocks and
// collects the distinct output fingerprints. This measures path diversity:
// how many observationally different execution paths the input corpus
// exercises. Combined with Blocks (the static block count), it gives a
// lower bound on code coverage.
func (k *Kernel) ProfilePaths(name string, argSets [][]byte, resultOffsets []int) ([]PathFingerprint, error) {
idx, ok := k.funcs[name]
if !ok {
return nil, fmt.Errorf("verify: function %q not found", name)
}
fl := k.img.Funcs[idx]
seen := map[string]bool{}
var paths []PathFingerprint
for _, args := range argSets {
if len(args) < fl.Args {
return nil, fmt.Errorf("verify: %s: arg block too small", name)
}
out, err := k.CallFunc(name, args)
if err != nil {
return nil, err
}
fp := PathFingerprint{}
key := ""
for _, off := range resultOffsets {
v := GetUint64(out, off)
fp.Results = append(fp.Results, v)
key += fmt.Sprintf("%016x", v)
}
if !seen[key] {
seen[key] = true
paths = append(paths, fp)
}
}
return paths, nil
}
+1 -1
View File
@@ -177,7 +177,7 @@ func (k *Kernel) FuzzFunc(name string, sig funcSig, goCode []byte, iterations in
return result
}
for i := 0; i < iterations; i++ {
for i := range iterations {
// Generate inputs and build TWO independent arg blocks (one per
// version) so that functions which write to their arguments
// (e.g. histogram increments) don't corrupt the other's input.
+15 -1
View File
@@ -38,6 +38,17 @@ func GroundTruthLOONG64(path string) (map[string][]byte, error) {
return groundTruthArch(path, "loong64")
}
// goRoot locates the GOROOT of the `go` binary on PATH: the build-time
// runtime.GOROOT is meaningless for a relocated binary, so the toolchain is
// asked directly.
func goRoot() (string, error) {
out, err := exec.Command("go", "env", "GOROOT").Output()
if err != nil {
return "", fmt.Errorf("verify: go env GOROOT: %w", err)
}
return strings.TrimSpace(string(out)), nil
}
// GroundTruthARM64 assembles the given .s file with the Go toolchain in
// AArch64 cross-assembly mode (GOARCH=arm64).
func GroundTruthARM64(path string) (map[string][]byte, error) {
@@ -45,7 +56,10 @@ func GroundTruthARM64(path string) (map[string][]byte, error) {
}
func groundTruthArch(path, goarch string) (map[string][]byte, error) {
goroot := runtime.GOROOT()
goroot, err := goRoot()
if err != nil {
return nil, err
}
asmBin := filepath.Join(goroot, "pkg", "tool", runtime.GOOS+"_"+runtime.GOARCH, "asm")
if _, err := os.Stat(asmBin); err != nil {
return nil, fmt.Errorf("verify: go tool asm not found at %s: %w", asmBin, err)
+1 -1
View File
@@ -144,7 +144,7 @@ func (k *Kernel) FuzzFuncChecked(name string, sig funcSig, iterations int, seed
}
violations := 0
for i := 0; i < iterations; i++ {
for i := range iterations {
gasmArgs, _, bufs := genDualArgs(rng, sig, fl.Args)
result.CrashInput = gasmArgs