feat(bna): implement fix-labels command

The fix-labels command will fix a symbol file by automatically renaming
labels matching a certain regex, and looking for the captured parameter
in a "fixlist" i.e. a newline delimited list of OG names.
This commit is contained in:
phaneron 2026-04-29 03:13:52 -04:00
parent d020e6ba36
commit cbb42eb953
6 changed files with 145 additions and 4 deletions

View file

@ -2,6 +2,7 @@ package cmd
import ( import (
_ "github.com/thunderbrewhq/binana/go/app/cmd/add_symbol" _ "github.com/thunderbrewhq/binana/go/app/cmd/add_symbol"
_ "github.com/thunderbrewhq/binana/go/app/cmd/fix_labels"
_ "github.com/thunderbrewhq/binana/go/app/cmd/lint" _ "github.com/thunderbrewhq/binana/go/app/cmd/lint"
_ "github.com/thunderbrewhq/binana/go/app/cmd/make" _ "github.com/thunderbrewhq/binana/go/app/cmd/make"
_ "github.com/thunderbrewhq/binana/go/app/cmd/make_samples" _ "github.com/thunderbrewhq/binana/go/app/cmd/make_samples"

View file

@ -0,0 +1,37 @@
package fix_labels
import (
"github.com/spf13/cobra"
"github.com/thunderbrewhq/binana/go/app"
"github.com/thunderbrewhq/binana/go/app/cmd/root"
"github.com/thunderbrewhq/binana/go/app/util"
)
var fix_labels_cmd = cobra.Command{
Use: "fix-labels fixlist capture-regex fix-pattern",
Args: cobra.MinimumNArgs(3),
Short: "quickly edit symbols by regex matching tokens against a list of known good symbols",
Run: run_fix_labels_cmd,
}
func init() {
f := fix_labels_cmd.Flags()
f.StringP("symbols", "s", "", "optional: specify a file to read from instead of stdin")
root.RootCmd.AddCommand(&fix_labels_cmd)
}
func run_fix_labels_cmd(cmd *cobra.Command, args []string) {
f := cmd.Flags()
var (
err error
params util.FixLabelsParams
)
params.Input, err = f.GetString("symbols")
if err != nil {
app.Fatal(err)
}
params.FixList = args[0]
params.Capture = args[1]
params.FixPattern = args[2]
util.FixLabels(&params)
}

View file

@ -24,7 +24,7 @@ func init() {
f.StringSlice("program", nil, "a list of programs to return tokens for") f.StringSlice("program", nil, "a list of programs to return tokens for")
f.StringSlice("os", nil, "a list of kernel names to return tokens for (windows, darwin, linux)") f.StringSlice("os", nil, "a list of kernel names to return tokens for (windows, darwin, linux)")
f.StringSlice("arch", nil, "a list of CPU architectures to return tokens for (ppc, 386, amd64)") f.StringSlice("arch", nil, "a list of CPU architectures to return tokens for (ppc, 386, amd64)")
f.String("present", "normal", "control the way tokens are presented to console (normal, name-only)") f.String("present", "normal", "control the way tokens are presented to console (name, sample-name)")
f.Bool("quote", false, "quote strings before presenting") f.Bool("quote", false, "quote strings before presenting")
root.RootCmd.AddCommand(&query_cmd) root.RootCmd.AddCommand(&query_cmd)
} }

View file

@ -11,6 +11,7 @@ import (
type linter struct { type linter struct {
warnings uint64 warnings uint64
named_functions_count uint64 named_functions_count uint64
typed_function_count uint64
} }
func (linter *linter) warn(s *symbols.TableEntry, f string, args ...any) { func (linter *linter) warn(s *symbols.TableEntry, f string, args ...any) {
@ -54,6 +55,10 @@ func Lint(params *LintParams) {
linter.warn(entry, "does not have an end address\n") linter.warn(entry, "does not have an end address\n")
} }
} }
if entry.Symbol.DataType != "" {
linter.typed_function_count++
}
} }
} }
@ -62,4 +67,9 @@ func Lint(params *LintParams) {
fmt.Printf("%d out of %d functions named (%f%%)\n", linter.named_functions_count, Profile.Info.FunctionCount, ratio*100.0) fmt.Printf("%d out of %d functions named (%f%%)\n", linter.named_functions_count, Profile.Info.FunctionCount, ratio*100.0)
fmt.Printf("%d warnings generated\n", linter.warnings) fmt.Printf("%d warnings generated\n", linter.warnings)
} }
if linter.named_functions_count != 0 {
typed_ratio := float64(linter.typed_function_count) / float64(linter.named_functions_count)
fmt.Printf("%d out of %d (%f%%) named functions have type information", linter.typed_function_count, linter.named_functions_count, typed_ratio*100.0)
}
} }

93
go/app/util/fix-labels.go Normal file
View file

@ -0,0 +1,93 @@
package util
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strings"
"github.com/thunderbrewhq/binana/go/symbols"
)
type FixLabelsParams struct {
// the input symbols file
Input string
// the list of fixed symbol names
FixList string
// regex to capture a word that will be matched against the fixlist
Capture string
// pattern to insert the match into so it may be checked against the fixlist
FixPattern string
}
func read_fixlist(path string) ([]string, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines, scanner.Err()
}
func FixLabels(params *FixLabelsParams) {
capture := regexp.MustCompile(params.Capture)
var (
input io.Reader
input_file *os.File
fixlist []string
table symbols.Table
err error
)
if params.Input != "" {
input_file, err = os.Open(params.Input)
if err != nil {
return
}
input = input_file
} else {
input = os.Stdin
}
if params.FixList == "" {
panic("must have fix list")
}
fixlist, err = read_fixlist(params.FixList)
if err != nil {
panic(err)
}
table.Init()
if err = table.LoadFile(params.Input, input); err != nil {
return
}
if input_file != nil {
input_file.Close()
}
for entry := range table.Entries() {
symbol := entry.Symbol
matches := capture.FindAllStringSubmatch(symbol.Name, 1)
if len(matches) > 0 {
//fmt.Fprintln(os.Stderr, "matches: ", matches)
match := matches[0][1]
fixed_match := strings.Replace(params.FixPattern, "$", match, 1)
for _, fix := range fixlist {
if strings.ToUpper(fixed_match) == strings.ToUpper(fix) {
symbol.Name = fix
break
}
}
}
fmt.Println(symbol.String())
}
}

View file

@ -39,7 +39,7 @@ type QueryParams struct {
MaxBuild uint32 MaxBuild uint32
// Regular expression for tokens (symbols/type information) // Regular expression for tokens (symbols/type information)
Token string Token string
// If true, Token is a POSIX regular expression // If true, Token is a RE2 regular expression
RegEx bool RegEx bool
} }
@ -202,9 +202,9 @@ func Query(params *QueryParams) {
var token_query token_query var token_query token_query
token_query.params = params token_query.params = params
if params.RegEx { if params.RegEx {
token_query.token_regexp = regexp.MustCompilePOSIX(token_query.params.Token) token_query.token_regexp = regexp.MustCompile(token_query.params.Token)
} else { } else {
token_query.token_regexp = regexp.MustCompilePOSIX(regexp.QuoteMeta(params.Token)) token_query.token_regexp = regexp.MustCompile(regexp.QuoteMeta(params.Token))
} }
if err := token_query.load_sample_database(); err != nil { if err := token_query.load_sample_database(); err != nil {