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

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"log"
     7  	"sort"
     8  
     9  	"github.com/decomp/exp/bin"
    10  	"github.com/decomp/exp/lift/x86"
    11  	"github.com/graphism/simple"
    12  	"github.com/llir/llvm/ir"
    13  	"github.com/llir/llvm/ir/value"
    14  	"github.com/pkg/errors"
    15  	"gonum.org/v1/gonum/graph"
    16  	"gonum.org/v1/gonum/graph/encoding"
    17  	"gonum.org/v1/gonum/graph/encoding/dot"
    18  )
    19  
    20  // Source represents a source file.
    21  type Source struct {
    22  	Name  string
    23  	Start bin.Address
    24  	End   bin.Address
    25  }
    26  
    27  var sources = []Source{
    28  	{Name: "_crt.cpp", Start: 0x401000, End: 0x401029},
    29  	{Name: "appfat.cpp", Start: 0x40102A, End: 0x401DA3},
    30  	{Name: "automap.cpp", Start: 0x401DA4, End: 0x40311A},
    31  	{Name: "capture.cpp", Start: 0x40311B, End: 0x4034D8},
    32  	{Name: "codec.cpp", Start: 0x4034D9, End: 0x4037D3},
    33  	{Name: "control.cpp", Start: 0x4037D4, End: 0x407409},
    34  	{Name: "cursor.cpp", Start: 0x40740A, End: 0x4084A5},
    35  	{Name: "dead.cpp", Start: 0x4084A6, End: 0x4086F3},
    36  	{Name: "debug.cpp", Start: 0x4086F4, End: 0x4087B0},
    37  	{Name: "diablo.cpp", Start: 0x4087B1, End: 0x40ACAC},
    38  	{Name: "doom.cpp", Start: 0x40ACAD, End: 0x40ADD5},
    39  	{Name: "drlg_l1.cpp", Start: 0x40ADD6, End: 0x40D356},
    40  	{Name: "drlg_l2.cpp", Start: 0x40D357, End: 0x40FF80},
    41  	{Name: "drlg_l3.cpp", Start: 0x40FF81, End: 0x412654},
    42  	{Name: "drlg_l4.cpp", Start: 0x412655, End: 0x415097},
    43  	{Name: "dthread.cpp", Start: 0x415098, End: 0x415361},
    44  	{Name: "dx.cpp", Start: 0x415362, End: 0x4158A8},
    45  	{Name: "effects.cpp", Start: 0x4158A9, End: 0x415F42},
    46  	{Name: "encrypt.cpp", Start: 0x415F43, End: 0x4161FB},
    47  	{Name: "engine.cpp", Start: 0x4161FC, End: 0x41804D},
    48  	{Name: "error.cpp", Start: 0x41804E, End: 0x4182AC},
    49  	{Name: "exception.cpp", Start: 0x4182AD, End: 0x418865},
    50  	{Name: "gamemenu.cpp", Start: 0x418866, End: 0x418C8A},
    51  	{Name: "gendung.cpp", Start: 0x418C8B, End: 0x419E8A},
    52  	{Name: "gmenu.cpp", Start: 0x419E8B, End: 0x41A552},
    53  	{Name: "help.cpp", Start: 0x41A553, End: 0x41A7B2},
    54  	{Name: "init.cpp", Start: 0x41A7B3, End: 0x41B18F},
    55  	{Name: "interfac.cpp", Start: 0x41B190, End: 0x41B813},
    56  	{Name: "inv.cpp", Start: 0x41B814, End: 0x41F095},
    57  	{Name: "items.cpp", Start: 0x41F096, End: 0x425442},
    58  	{Name: "lighting.cpp", Start: 0x425443, End: 0x426563},
    59  	{Name: "loadsave.cpp", Start: 0x426564, End: 0x4279F1},
    60  	{Name: "log.cpp", Start: 0x4279F2, End: 0x427E0D},
    61  	{Name: "mainmenu.cpp", Start: 0x427E0E, End: 0x428055},
    62  	{Name: "minitext.cpp", Start: 0x428056, End: 0x4283BF},
    63  	{Name: "missiles.cpp", Start: 0x4283C0, End: 0x430FDE},
    64  	{Name: "monster.cpp", Start: 0x430FDF, End: 0x43AD32},
    65  	{Name: "movie.cpp", Start: 0x43AD33, End: 0x43AE8F},
    66  	{Name: "mpqapi.cpp", Start: 0x43AE90, End: 0x43BBA3},
    67  	{Name: "msg.cpp", Start: 0x43BBA4, End: 0x43F848},
    68  	{Name: "msgcmd.cpp", Start: 0x43F849, End: 0x43FAC3},
    69  	{Name: "multi.cpp", Start: 0x43FAC4, End: 0x440DAD},
    70  	{Name: "nthread.cpp", Start: 0x440DAE, End: 0x44121C},
    71  	{Name: "objects.cpp", Start: 0x44121D, End: 0x448754},
    72  	{Name: "hero.cpp", Start: 0x448755, End: 0x448DF4},
    73  	{Name: "palette.cpp", Start: 0x448DF5, End: 0x4493D3},
    74  	{Name: "path.cpp", Start: 0x4493D4, End: 0x4498EB},
    75  	{Name: "pfile.cpp", Start: 0x4498EC, End: 0x44A8E5},
    76  	{Name: "player.cpp", Start: 0x44A8E6, End: 0x450D32},
    77  	{Name: "plrmsg.cpp", Start: 0x450D33, End: 0x450FFD},
    78  	{Name: "portal.cpp", Start: 0x450FFE, End: 0x45138D},
    79  	{Name: "quests.cpp", Start: 0x45138E, End: 0x452830},
    80  	{Name: "restricted.cpp", Start: 0x452831, End: 0x452974},
    81  	{Name: "scrollrt.cpp", Start: 0x452975, End: 0x456624},
    82  	{Name: "setmaps.cpp", Start: 0x456625, End: 0x456A15},
    83  	{Name: "sha1.cpp", Start: 0x456A16, End: 0x456CBA},
    84  	{Name: "sound.cpp", Start: 0x456CBB, End: 0x45744D},
    85  	{Name: "spells.cpp", Start: 0x45744E, End: 0x457A00},
    86  	{Name: "stores.cpp", Start: 0x457A01, End: 0x45C198},
    87  	{Name: "sync.cpp", Start: 0x45C199, End: 0x45C86F},
    88  	{Name: "themes.cpp", Start: 0x45C870, End: 0x45E08B},
    89  	{Name: "tmsg.cpp", Start: 0x45E08C, End: 0x45E150},
    90  	{Name: "town.cpp", Start: 0x45E151, End: 0x46019A},
    91  	{Name: "towners.cpp", Start: 0x46019B, End: 0x4618A4},
    92  	{Name: "track.cpp", Start: 0x4618A5, End: 0x4619A6},
    93  	{Name: "trigs.cpp", Start: 0x4619A7, End: 0x462C6C},
    94  	{Name: "wave.cpp", Start: 0x462C6D, End: 0x46305F},
    95  	{Name: "world.cpp", Start: 0x463060, End: 0x469719},
    96  	{Name: "_crt.cpp", Start: 0x46971A, End: 0x47746F},
    97  	{Name: "pkware.cpp", Start: 0x477470, End: 0x478FFF},
    98  }
    99  
   100  // Node represents a call site node in a call graph.
   101  type Node struct {
   102  	graph.Node
   103  	Name string
   104  }
   105  
   106  // Attributes returns the DOT attributes of the node.
   107  func (n Node) Attributes() []encoding.Attribute {
   108  	return []encoding.Attribute{
   109  		{Key: "label", Value: fmt.Sprintf("%q", n.Name)},
   110  	}
   111  }
   112  
   113  func genCallGraph(funcs map[bin.Address]*x86.Func) error {
   114  	for _, source := range sources {
   115  		nodes := make(map[string]graph.Node)
   116  		g := simple.NewDirectedGraph()
   117  		fmt.Println("source:", source.Name)
   118  		for addr, f := range funcs {
   119  			if !(source.Start <= addr && addr <= source.End) {
   120  				continue
   121  			}
   122  			fmt.Println("   func:", addr, f.Name())
   123  			fn, ok := nodes[f.Name()]
   124  			if !ok {
   125  				node := Node{
   126  					Node: g.NewNode(),
   127  					Name: f.Name(),
   128  				}
   129  				fn = node
   130  				nodes[f.Name()] = node
   131  				g.AddNode(fn)
   132  			}
   133  			// Callers.
   134  			names := getCallerNames(funcs, f.Name())
   135  			for _, name := range names {
   136  				caller, ok := nodes[name]
   137  				if !ok {
   138  					node := Node{
   139  						Node: g.NewNode(),
   140  						Name: name,
   141  					}
   142  					caller = node
   143  					nodes[name] = node
   144  					g.AddNode(caller)
   145  				}
   146  				e := simple.Edge{
   147  					F: caller,
   148  					T: fn,
   149  				}
   150  				g.SetEdge(e)
   151  			}
   152  			// Callees.
   153  			names = getCalleeNames(f.Func)
   154  			for _, name := range names {
   155  				callee, ok := nodes[name]
   156  				if !ok {
   157  					node := Node{
   158  						Node: g.NewNode(),
   159  						Name: name,
   160  					}
   161  					callee = node
   162  					nodes[name] = node
   163  					g.AddNode(callee)
   164  				}
   165  				e := simple.Edge{
   166  					F: fn,
   167  					T: callee,
   168  				}
   169  				g.SetEdge(e)
   170  			}
   171  		}
   172  		data, err := dot.Marshal(g, "", "", "\t")
   173  		if err != nil {
   174  			return errors.WithStack(err)
   175  		}
   176  		path := fmt.Sprintf("call_graphs/%s.dot", source.Name)
   177  		if err := ioutil.WriteFile(path, data, 0644); err != nil {
   178  			return errors.WithStack(err)
   179  		}
   180  	}
   181  	return nil
   182  }
   183  
   184  // calls returns the callers and callees of the given functions; where callers
   185  // maps from callee to caller, and callees maps from caller to callee.
   186  func calls(funcs map[bin.Address]*x86.Func) (callers, callees map[string]string) {
   187  	callers = make(map[string]string)
   188  	callees = make(map[string]string)
   189  	for _, f := range funcs {
   190  		caller := f.Name()
   191  		for _, block := range f.Blocks {
   192  			for _, inst := range block.Insts {
   193  				if inst, ok := inst.(*ir.InstCall); ok {
   194  					if c, ok := inst.Callee.(value.Named); ok {
   195  						callee := c.Name()
   196  						callers[callee] = caller
   197  						callees[caller] = callee
   198  					} else {
   199  						log.Fatalf("unable to locate name of callee `%v` in function %q", inst.Callee, caller)
   200  					}
   201  				}
   202  			}
   203  		}
   204  	}
   205  	return callers, callees
   206  }
   207  
   208  func getCallerNames(funcs map[bin.Address]*x86.Func, name string) []string {
   209  	m := make(map[string]bool)
   210  	for _, f := range funcs {
   211  		for _, block := range f.Blocks {
   212  			for _, inst := range block.Insts {
   213  				if inst, ok := inst.(*ir.InstCall); ok {
   214  					if c, ok := inst.Callee.(value.Named); ok {
   215  						if c.Name() == name {
   216  							m[f.Name()] = true
   217  						}
   218  					} else {
   219  						log.Fatalf("unable to locate name of callee `%v` in function %q", inst.Callee, f.Name())
   220  					}
   221  				}
   222  			}
   223  		}
   224  	}
   225  	var names []string
   226  	for name := range m {
   227  		names = append(names, name)
   228  	}
   229  	sort.Strings(names)
   230  	return names
   231  }
   232  
   233  func getCalleeNames(f *ir.Func) []string {
   234  	m := make(map[string]bool)
   235  	for _, block := range f.Blocks {
   236  		for _, inst := range block.Insts {
   237  			if inst, ok := inst.(*ir.InstCall); ok {
   238  				if c, ok := inst.Callee.(value.Named); ok {
   239  					m[c.Name()] = true
   240  				} else {
   241  					log.Fatalf("unable to locate name of callee `%v`", inst.Callee)
   242  				}
   243  			}
   244  		}
   245  	}
   246  	var names []string
   247  	for name := range m {
   248  		names = append(names, name)
   249  	}
   250  	sort.Strings(names)
   251  	return names
   252  }