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

     1  // The h2ll tool converts C headers to LLVM IR function declarations  (*.h ->
     2  // *.ll).
     3  package main
     4  
     5  import (
     6  	"bufio"
     7  	"encoding/json"
     8  	"flag"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"log"
    12  	"os"
    13  	"sort"
    14  
    15  	"github.com/decomp/exp/bin"
    16  	"github.com/llir/llvm/ir"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  func usage() {
    21  	const use = `
    22  Convert C headers to LLVM IR function declarations (*.h -> *.ll).
    23  
    24  Usage:
    25  
    26  	h2ll [OPTION]... FILE.h
    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  		// jsonPath specifies the path to a JSON file with function signatures.
    38  		jsonPath string
    39  		// output specifies the output path.
    40  		output string
    41  	)
    42  	flag.StringVar(&jsonPath, "sigs", "sigs.json", "JSON file with function signatures")
    43  	flag.StringVar(&output, "o", "", "output path")
    44  	flag.Parse()
    45  	flag.Usage = usage
    46  	flag.Parse()
    47  	if flag.NArg() != 1 {
    48  		flag.Usage()
    49  		os.Exit(1)
    50  	}
    51  	hPath := flag.Arg(0)
    52  
    53  	// Parse JSON file containing function signatures.
    54  	sigs, funcAddrs, err := parseSigs(jsonPath)
    55  	if err != nil {
    56  		log.Fatalf("%+v", err)
    57  	}
    58  	// Convert function signatures to LLVM IR.
    59  	buf, err := ioutil.ReadFile(hPath)
    60  	if err != nil {
    61  		log.Fatalf("%+v", err)
    62  	}
    63  	input := string(buf)
    64  	old, err := compile(input)
    65  	if err != nil {
    66  		log.Fatalf("%+v", err)
    67  	}
    68  	funcs, err := llFuncSigs(old, sigs, funcAddrs)
    69  	if err != nil {
    70  		log.Fatalf("%+v", err)
    71  	}
    72  	// Store C header output.
    73  	w := os.Stdout
    74  	if len(output) > 0 {
    75  		f, err := os.Create(output)
    76  		if err != nil {
    77  			log.Fatal(err)
    78  		}
    79  		defer f.Close()
    80  		w = f
    81  	}
    82  	module := ir.NewModule()
    83  	module.TypeDefs = old.TypeDefs
    84  	module.Funcs = funcs
    85  	if _, err := w.WriteString(module.String()); err != nil {
    86  		log.Fatalf("%+v", err)
    87  	}
    88  }
    89  
    90  // parseSigs parses the given JSON file containing function signatures.
    91  func parseSigs(jsonPath string) (map[bin.Address]FuncSig, []bin.Address, error) {
    92  	// Parse file.
    93  	sigs := make(map[bin.Address]FuncSig)
    94  	if err := parseJSON(jsonPath, &sigs); err != nil {
    95  		return nil, nil, errors.WithStack(err)
    96  	}
    97  	var funcAddrs []bin.Address
    98  	for funcAddr := range sigs {
    99  		funcAddrs = append(funcAddrs, funcAddr)
   100  	}
   101  	sort.Sort(bin.Addresses(funcAddrs))
   102  	return sigs, funcAddrs, nil
   103  }
   104  
   105  // FuncSig represents a function signature.
   106  type FuncSig struct {
   107  	// Function name.
   108  	Name string `json:"name"`
   109  	// Function signature.
   110  	Sig string `json:"sig"`
   111  }
   112  
   113  // ### [ Helper functions ] ####################################################
   114  
   115  // parseJSON parses the given JSON file and stores the result into v.
   116  func parseJSON(jsonPath string, v interface{}) error {
   117  	f, err := os.Open(jsonPath)
   118  	if err != nil {
   119  		return errors.WithStack(err)
   120  	}
   121  	defer f.Close()
   122  	br := bufio.NewReader(f)
   123  	dec := json.NewDecoder(br)
   124  	return dec.Decode(v)
   125  }