mirror of
https://github.com/thunderbrewhq/binana.git
synced 2026-03-22 22:00:13 +00:00
feat(bna): add 'as' subcommand to add symbols
Some checks failed
Push / build (push) Has been cancelled
Some checks failed
Push / build (push) Has been cancelled
This commit is contained in:
parent
b62710fe15
commit
362bf7f31b
8 changed files with 300 additions and 3 deletions
71
go/app/cmd/add_symbol/add_symbol.go
Normal file
71
go/app/cmd/add_symbol/add_symbol.go
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
package add_symbol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/thunderbrewhq/binana/go/app"
|
||||||
|
"github.com/thunderbrewhq/binana/go/app/cmd/root"
|
||||||
|
"github.com/thunderbrewhq/binana/go/app/profile"
|
||||||
|
"github.com/thunderbrewhq/binana/go/symbols"
|
||||||
|
)
|
||||||
|
|
||||||
|
func as_func(cmd *cobra.Command, args []string) {
|
||||||
|
f := cmd.Flags()
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
params profile.AddSymbolParams
|
||||||
|
)
|
||||||
|
params.Profile = args[0]
|
||||||
|
params.Group, err = f.GetString("group")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: infer the group
|
||||||
|
if params.Group == "" {
|
||||||
|
app.Fatal("pass in the group (-g, --group) that will store the symbol(s)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(args) < 2 {
|
||||||
|
// read normally formatted symbols from stdin
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
var symbol symbols.Symbol
|
||||||
|
if err = symbol.Parse(line); err != nil {
|
||||||
|
app.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Symbols = append(params.Symbols, symbol)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// use args to add symbol
|
||||||
|
if len(args) < 4 {
|
||||||
|
cmd.Help()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Symbols = make([]symbols.Symbol, 1)
|
||||||
|
if err = params.Symbols[0].ParseCommandLine(args[1:]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params.Symbols[0].Comment, err = f.GetString("comment")
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.AddSymbol(¶ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
var as_cmd = cobra.Command{
|
||||||
|
Use: "as profile [symbol_name] [address] [l|f] [type=...]",
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
|
Short: "Add symbols to profile",
|
||||||
|
Long: "Adds symbol passed through command-line arguments, or add one or more normally-formatted symbols by passing them through stdin",
|
||||||
|
Run: as_func,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
f := as_cmd.Flags()
|
||||||
|
f.StringP("group", "g", "", "the profile symbol group to record symbols into (e.g. 'feature1' to record into profile/<profile>/symbol/feature1)")
|
||||||
|
f.StringP("comment", "m", "", "the comment to pass to the symbol")
|
||||||
|
root.RootCmd.AddCommand(&as_cmd)
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "github.com/thunderbrewhq/binana/go/app/cmd/add_symbol"
|
||||||
_ "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/root"
|
||||||
_ "github.com/thunderbrewhq/binana/go/app/cmd/tidy"
|
_ "github.com/thunderbrewhq/binana/go/app/cmd/tidy"
|
||||||
|
|
||||||
"github.com/thunderbrewhq/binana/go/app"
|
"github.com/thunderbrewhq/binana/go/app"
|
||||||
"github.com/thunderbrewhq/binana/go/app/cmd/root"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/thunderbrewhq/binana/go/app"
|
"github.com/thunderbrewhq/binana/go/app"
|
||||||
|
"github.com/thunderbrewhq/binana/go/app/cmd/root"
|
||||||
"github.com/thunderbrewhq/binana/go/app/profile"
|
"github.com/thunderbrewhq/binana/go/app/profile"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -16,6 +17,7 @@ func init() {
|
||||||
f := lint_cmd.Flags()
|
f := lint_cmd.Flags()
|
||||||
f.Bool("bounds", false, "check for bad function boundaries")
|
f.Bool("bounds", false, "check for bad function boundaries")
|
||||||
f.Bool("constructors", false, "check for outdated class constructor names")
|
f.Bool("constructors", false, "check for outdated class constructor names")
|
||||||
|
root.RootCmd.AddCommand(&lint_cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func lint_func(cmd *cobra.Command, args []string) {
|
func lint_func(cmd *cobra.Command, args []string) {
|
||||||
|
|
|
||||||
132
go/app/profile/add-symbol.go
Normal file
132
go/app/profile/add-symbol.go
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/thunderbrewhq/binana/go/app"
|
||||||
|
"github.com/thunderbrewhq/binana/go/symbols"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AddSymbolParams struct {
|
||||||
|
Profile string
|
||||||
|
Group string
|
||||||
|
Symbols []symbols.Symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSymbol(params *AddSymbolParams) {
|
||||||
|
Open(params.Profile)
|
||||||
|
|
||||||
|
// Detect collisions
|
||||||
|
// TODO: check if address exists within a function's range
|
||||||
|
for i, symbol := range params.Symbols {
|
||||||
|
entry, err := Profile.Symbols.Lookup(symbol.StartAddress)
|
||||||
|
if err == nil {
|
||||||
|
app.Fatal(fmt.Errorf(
|
||||||
|
"profile: cannot add symbol '%s' (--:%d), another symbol '%s' (%s:%d) already exists at the same address (%08X)",
|
||||||
|
symbol.Name,
|
||||||
|
i+1,
|
||||||
|
entry.Symbol.Name,
|
||||||
|
entry.Filename,
|
||||||
|
entry.Linenumber,
|
||||||
|
symbol.StartAddress,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load all symbols, and rewrite them
|
||||||
|
group_symbol_directory := filepath.Join(Profile.Directory, "symbol", params.Group)
|
||||||
|
var group_symbols symbols.Table
|
||||||
|
group_symbols.Init()
|
||||||
|
|
||||||
|
group_symbols_file_info, err := os.Stat(group_symbol_directory)
|
||||||
|
if err != nil {
|
||||||
|
err = os.MkdirAll(group_symbol_directory, 0755)
|
||||||
|
if err != nil {
|
||||||
|
app.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !group_symbols_file_info.IsDir() {
|
||||||
|
app.Fatal(fmt.Errorf("profile: symbol group '%s' has invalid directory structure", params.Group))
|
||||||
|
}
|
||||||
|
if err = group_symbols.Load(group_symbol_directory); err != nil {
|
||||||
|
app.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, symbol := range params.Symbols {
|
||||||
|
var entry symbols.TableEntry
|
||||||
|
entry.Filename = "--"
|
||||||
|
entry.Linenumber = i + 1
|
||||||
|
entry.Symbol = symbol
|
||||||
|
if err := group_symbols.Insert(&entry); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label_file_name := filepath.Join(group_symbol_directory, "label.sym")
|
||||||
|
func_file_name := filepath.Join(group_symbol_directory, "func.sym")
|
||||||
|
label_file_tmp_name := label_file_name + ".tmp"
|
||||||
|
func_file_tmp_name := func_file_name + ".tmp"
|
||||||
|
label_file, err := os.Create(label_file_tmp_name)
|
||||||
|
if err != nil {
|
||||||
|
app.Fatal(err)
|
||||||
|
}
|
||||||
|
func_file, err := os.Create(func_file_tmp_name)
|
||||||
|
if err != nil {
|
||||||
|
app.Fatal(err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
labels, functions int
|
||||||
|
)
|
||||||
|
|
||||||
|
save_loop:
|
||||||
|
for entry := range group_symbols.Entries() {
|
||||||
|
switch entry.Symbol.Kind {
|
||||||
|
case symbols.DataLabel:
|
||||||
|
labels++
|
||||||
|
if _, err = entry.Symbol.WriteTo(label_file); err != nil {
|
||||||
|
break save_loop
|
||||||
|
}
|
||||||
|
if _, err = label_file.Write([]byte("\n")); err != nil {
|
||||||
|
break save_loop
|
||||||
|
}
|
||||||
|
case symbols.Function:
|
||||||
|
functions++
|
||||||
|
if _, err = entry.Symbol.WriteTo(func_file); err != nil {
|
||||||
|
break save_loop
|
||||||
|
}
|
||||||
|
if _, err = func_file.Write([]byte("\n")); err != nil {
|
||||||
|
break save_loop
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unhandled function kind %c", entry.Symbol.Kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
label_file.Close()
|
||||||
|
func_file.Close()
|
||||||
|
os.Remove(label_file_name)
|
||||||
|
os.Remove(func_file_name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
label_file.Close()
|
||||||
|
func_file.Close()
|
||||||
|
if labels == 0 {
|
||||||
|
os.Remove(label_file_name)
|
||||||
|
os.Remove(label_file_tmp_name)
|
||||||
|
} else {
|
||||||
|
if err = os.Rename(label_file_tmp_name, label_file_name); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if functions == 0 {
|
||||||
|
os.Remove(func_file_name)
|
||||||
|
os.Remove(func_file_tmp_name)
|
||||||
|
} else {
|
||||||
|
if err = os.Rename(func_file_tmp_name, func_file_name); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
go/app/util/tidy-symbols.go
Normal file
4
go/app/util/tidy-symbols.go
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
type TidySymbolFileParams struct {
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package symbols
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ErrSymbolNotFound = fmt.Errorf("symbols: symbol not found")
|
||||||
ErrDuplicateSymbol = fmt.Errorf("symbols: a symbol already exists at that address")
|
ErrDuplicateSymbol = fmt.Errorf("symbols: a symbol already exists at that address")
|
||||||
ErrLineLeftEmpty = fmt.Errorf("symbols: line was left empty")
|
ErrLineLeftEmpty = fmt.Errorf("symbols: line was left empty")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ func parse_attributes(attribute_columns []string) (attributes map[string]string,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
key, value_start, found := strings.Cut(attribute_column, "=")
|
key, value_start, found := strings.Cut(attribute_column, "=")
|
||||||
if !found {
|
if !found {
|
||||||
err = fmt.Errorf("extraneous column: '%s'", attribute_column)
|
err = fmt.Errorf("extraneous column: '%s'", attribute_column)
|
||||||
|
|
@ -134,7 +133,84 @@ func (symbol *Symbol) Parse(line string) (err error) {
|
||||||
var attributes map[string]string
|
var attributes map[string]string
|
||||||
attributes, err = parse_attributes(extra_columns)
|
attributes, err = parse_attributes(extra_columns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("symfile: (*entry).Parse: error parsing attribute: %w", err)
|
return fmt.Errorf("symbols: (*Symbol).Parse: error parsing attribute: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data_type, found := attributes["type"]; found {
|
||||||
|
symbol.DataType = data_type
|
||||||
|
}
|
||||||
|
|
||||||
|
if end_address, found := attributes["end"]; found {
|
||||||
|
symbol.EndAddress, err = strconv.ParseUint(end_address, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (symbol *Symbol) ParseCommandLine(columns []string) (err error) {
|
||||||
|
// validate
|
||||||
|
if len(columns) < min_columns {
|
||||||
|
// this line is discarded but not in error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
start_address uint64
|
||||||
|
comment_text string
|
||||||
|
)
|
||||||
|
|
||||||
|
// get name of symbol
|
||||||
|
name_column := columns[0]
|
||||||
|
if name_column == "" {
|
||||||
|
return fmt.Errorf("symbols: (*Symbol).Parse: entry has invalid name '%s", name_column)
|
||||||
|
}
|
||||||
|
|
||||||
|
start_address, err = strconv.ParseUint(columns[1], 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kind_column := columns[2]
|
||||||
|
if len(kind_column) != 1 {
|
||||||
|
return fmt.Errorf("symbols: (*Symbol).Parse: entry has invalid kind")
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := SymbolKind(kind_column[0])
|
||||||
|
|
||||||
|
if !slices.Contains(valid_kinds, kind) {
|
||||||
|
return fmt.Errorf("symfile: (*entry).Parse: entry has invalid kind")
|
||||||
|
}
|
||||||
|
|
||||||
|
// find index of comment column
|
||||||
|
index_of_comment := slices.Index(columns, ";")
|
||||||
|
|
||||||
|
var num_semantic_columns int
|
||||||
|
|
||||||
|
if index_of_comment != -1 {
|
||||||
|
num_semantic_columns = index_of_comment
|
||||||
|
comment_text_columns := columns[index_of_comment+1:]
|
||||||
|
comment_text = strings.Join(comment_text_columns, " ")
|
||||||
|
} else {
|
||||||
|
num_semantic_columns = len(columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start to build entry
|
||||||
|
symbol.Name = name_column
|
||||||
|
symbol.StartAddress = start_address
|
||||||
|
symbol.Kind = kind
|
||||||
|
symbol.Comment = comment_text
|
||||||
|
|
||||||
|
// build attributes
|
||||||
|
if num_semantic_columns > 3 {
|
||||||
|
extra_columns := columns[3:num_semantic_columns]
|
||||||
|
|
||||||
|
var attributes map[string]string
|
||||||
|
attributes, err = parse_attributes(extra_columns)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("symbols: (*Symbol).Parse: error parsing attribute: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if data_type, found := attributes["type"]; found {
|
if data_type, found := attributes["type"]; found {
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,16 @@ func (table *Table) LoadFile(name string, file io.Reader) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (table *Table) Lookup(address uint64) (entry *TableEntry, err error) {
|
||||||
|
var ok bool
|
||||||
|
entry, ok = table.entries[address]
|
||||||
|
if !ok {
|
||||||
|
err = fmt.Errorf("%w: %08X", ErrSymbolNotFound, address)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (table *Table) Insert(entry *TableEntry) (err error) {
|
func (table *Table) Insert(entry *TableEntry) (err error) {
|
||||||
conflicting_symbol, ok := table.entries[entry.Symbol.StartAddress]
|
conflicting_symbol, ok := table.entries[entry.Symbol.StartAddress]
|
||||||
if ok {
|
if ok {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue