github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/cmd/sigs2h/main.go (about)

     1  // The sigs2h tool converts function signatures to C headers (*.json -> *.h).
     2  package main
     3  
     4  import (
     5  	"bufio"
     6  	"encoding/json"
     7  	"flag"
     8  	"fmt"
     9  	"io"
    10  	"log"
    11  	"os"
    12  	"sort"
    13  	"text/template"
    14  
    15  	"github.com/decomp/exp/bin"
    16  	"github.com/mewkiz/pkg/errutil"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  func usage() {
    21  	const use = `
    22  Convert function signatures to empty C headers (*.json -> *.h).
    23  
    24  Usage:
    25  
    26  	sigs2h [OPTION]... FILE.json
    27  
    28  Flags:
    29  `
    30  	fmt.Fprint(os.Stderr, use[1:])
    31  	flag.PrintDefaults()
    32  }
    33  
    34  func main() {
    35  	// Parse command line flags.
    36  	var (
    37  		// output specifies the output path.
    38  		output string
    39  	)
    40  	flag.StringVar(&output, "o", "", "output path")
    41  	flag.Parse()
    42  	flag.Usage = usage
    43  	flag.Parse()
    44  	if flag.NArg() != 1 {
    45  		flag.Usage()
    46  		os.Exit(1)
    47  	}
    48  	jsonPath := flag.Arg(0)
    49  
    50  	// Store C header output.
    51  	w := os.Stdout
    52  	if len(output) > 0 {
    53  		f, err := os.Create(output)
    54  		if err != nil {
    55  			log.Fatal(err)
    56  		}
    57  		defer f.Close()
    58  		w = f
    59  	}
    60  	if err := convert(w, jsonPath); err != nil {
    61  		log.Fatalf("%+v", err)
    62  	}
    63  }
    64  
    65  // convert converts the the given function signatures to C headers.
    66  func convert(w io.Writer, jsonPath string) error {
    67  	// Parse file.
    68  	sigs := make(map[bin.Address]FuncSig)
    69  	if err := parseJSON(jsonPath, &sigs); err != nil {
    70  		return errors.WithStack(err)
    71  	}
    72  	var funcAddrs []bin.Address
    73  	for funcAddr := range sigs {
    74  		funcAddrs = append(funcAddrs, funcAddr)
    75  	}
    76  	sort.Sort(bin.Addresses(funcAddrs))
    77  
    78  	// Convert function signatures to C.
    79  	const source = `
    80  #include <stdint.h> // int8_t, ...
    81  #include <stdarg.h> // va_list
    82  #if __WORDSIZE == 64
    83  	typedef uint64_t size_t;
    84  #else
    85  	typedef uint32_t size_t;
    86  #endif
    87  
    88  #include "types.h"
    89  
    90  {{ range . }}
    91  // {{ .Addr }}
    92  {{ .Sig }} {}
    93  {{ end }}
    94  `
    95  	var funcSigs []Signature
    96  	for _, funcAddr := range funcAddrs {
    97  		sig := sigs[funcAddr]
    98  		s := sig.Sig
    99  		if len(s) == 0 {
   100  			s = fmt.Sprintf("void %s() /* signature missing */", sig.Name)
   101  		}
   102  		funcSig := Signature{
   103  			Addr: funcAddr,
   104  			Sig:  s,
   105  		}
   106  		funcSigs = append(funcSigs, funcSig)
   107  	}
   108  	t := template.New("signatures")
   109  	if _, err := t.Parse(source[1:]); err != nil {
   110  		return errors.WithStack(err)
   111  	}
   112  	if err := t.Execute(w, funcSigs); err != nil {
   113  		return errutil.Err(err)
   114  	}
   115  	return nil
   116  }
   117  
   118  // Signature represents a function signature.
   119  type Signature struct {
   120  	// Function address.
   121  	Addr bin.Address
   122  	// Function signature.
   123  	Sig string
   124  }
   125  
   126  // FuncSig represents a function signature.
   127  type FuncSig struct {
   128  	// Function name.
   129  	Name string `json:"name"`
   130  	// Function signature.
   131  	Sig string `json:"sig"`
   132  }
   133  
   134  // ### [ Helper functions ] ####################################################
   135  
   136  // parseJSON parses the given JSON file and stores the result into v.
   137  func parseJSON(jsonPath string, v interface{}) error {
   138  	f, err := os.Open(jsonPath)
   139  	if err != nil {
   140  		return errors.WithStack(err)
   141  	}
   142  	defer f.Close()
   143  	br := bufio.NewReader(f)
   144  	dec := json.NewDecoder(br)
   145  	return dec.Decode(v)
   146  }