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 }