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

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"os"
     6  	"os/exec"
     7  	"strings"
     8  
     9  	"github.com/decomp/exp/bin"
    10  	"github.com/llir/llvm/asm"
    11  	"github.com/llir/llvm/ir"
    12  	"github.com/llir/llvm/ir/metadata"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  // llFuncSigs translates the given function signatures from C to LLVM IR.
    17  func llFuncSigs(module *ir.Module, sigs map[bin.Address]FuncSig, funcAddrs []bin.Address) ([]*ir.Func, error) {
    18  	var funcs []*ir.Func
    19  	nameToFunc := make(map[string]*ir.Func)
    20  	for _, f := range module.Funcs {
    21  		if _, ok := nameToFunc[f.GlobalName]; ok {
    22  			return nil, errors.Errorf("function name %q already present", f.Ident())
    23  		}
    24  		nameToFunc[f.GlobalName] = f
    25  	}
    26  	for _, funcAddr := range funcAddrs {
    27  		sig := sigs[funcAddr]
    28  		f, ok := locateFunc(sig.Name, nameToFunc)
    29  		if !ok {
    30  			return nil, errors.Errorf("unable to locate function %q", sig.Name)
    31  		}
    32  		f.Blocks = nil
    33  		md := &metadata.Attachment{
    34  			Name: "addr",
    35  			Node: &metadata.Tuple{
    36  				Fields: []metadata.Field{&metadata.String{Value: funcAddr.String()}},
    37  			},
    38  		}
    39  		f.Metadata = append(f.Metadata, md)
    40  		funcs = append(funcs, f)
    41  	}
    42  	return funcs, nil
    43  }
    44  
    45  // locateFunc locates the named function.
    46  func locateFunc(funcName string, nameToFunc map[string]*ir.Func) (*ir.Func, bool) {
    47  	// IDA may include _imp prefix to imports.
    48  	//
    49  	//    a: ExitProcess
    50  	//    b: __imp_ExitProcess
    51  	if strings.HasPrefix(funcName, "__imp_") {
    52  		if f, ok := nameToFunc[funcName[len("__imp_"):]]; ok {
    53  			return f, true
    54  		}
    55  	}
    56  
    57  	// IDA seems to ignore a leading underscore in function names when printing
    58  	// the function signature.
    59  	//
    60  	//    a: _crt_cpp_init
    61  	//    b: crt_cpp_init
    62  	if strings.HasPrefix(funcName, "_") {
    63  		if f, ok := nameToFunc[funcName[len("_"):]]; ok {
    64  			return f, true
    65  		}
    66  	}
    67  	if f, ok := nameToFunc[funcName]; ok {
    68  		return f, true
    69  	}
    70  	// TODO: Fix handling of constructors and destructors.
    71  	switch funcName {
    72  	case "??1type_info@@UAE@XZ":
    73  		return locateFunc("type_info_create", nameToFunc)
    74  	case "??_Gtype_info@@UAEPAXI@Z":
    75  		return locateFunc("type_info_delete", nameToFunc)
    76  	}
    77  	//    a: WinMain
    78  	//    b: _WinMain@16
    79  	if pos := strings.IndexAny(funcName, "@"); pos != -1 {
    80  		return locateFunc(funcName[:pos], nameToFunc)
    81  	}
    82  	return nil, false
    83  }
    84  
    85  // compile compiles the given C source into LLVM IR.
    86  func compile(input string) (*ir.Module, error) {
    87  	out := &bytes.Buffer{}
    88  	cmd := exec.Command("clang", "-m32", "-S", "-emit-llvm", "-x", "c", "-Wno-return-type", "-Wno-invalid-noreturn", "-o", "-", "-")
    89  	cmd.Stdin = strings.NewReader(input)
    90  	cmd.Stdout = out
    91  	cmd.Stderr = os.Stderr
    92  	if err := cmd.Run(); err != nil {
    93  		return nil, errors.WithStack(err)
    94  	}
    95  	module, err := asm.ParseBytes("<stdin>", out.Bytes())
    96  	if err != nil {
    97  		return nil, errors.WithStack(err)
    98  	}
    99  	return module, nil
   100  }