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 }