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

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strconv"
     7  
     8  	"github.com/decomp/exp/bin"
     9  	"github.com/decomp/exp/disasm/x86"
    10  	"github.com/graphism/simple"
    11  	"github.com/pkg/errors"
    12  	"gonum.org/v1/gonum/graph"
    13  	"gonum.org/v1/gonum/graph/encoding"
    14  )
    15  
    16  // dumpCFG dumps the control flow graph of the given function.
    17  func dumpCFG(dis *x86.Disasm, f *x86.Func) (graph.Directed, error) {
    18  	// Index functions, basic blocks and instructions.
    19  	g := simple.NewDirectedGraph()
    20  	nodes := make(map[bin.Address]*Node)
    21  	for _, block := range f.Blocks {
    22  		id := strconv.Quote(block.Addr.String())
    23  		n := &Node{
    24  			Node:  g.NewNode(),
    25  			id:    id,
    26  			Attrs: make(Attrs),
    27  		}
    28  		if block.Addr == f.Addr {
    29  			n.Attrs["label"] = "entry"
    30  		}
    31  		nodes[block.Addr] = n
    32  		g.AddNode(n)
    33  	}
    34  	for _, block := range f.Blocks {
    35  		targets := dis.Targets(block.Term, f.Addr)
    36  		fmt.Println("block.Addr:", block.Addr)
    37  		fmt.Println("block.Term:", block.Term)
    38  		fmt.Println("targets:", targets)
    39  		fmt.Println()
    40  
    41  		from, ok := nodes[block.Addr]
    42  		if !ok {
    43  			panic(errors.Errorf("unable to locate basic block at %v", block.Addr))
    44  		}
    45  		for i, target := range targets {
    46  			to, ok := nodes[target]
    47  			if !ok {
    48  				return nil, errors.Errorf("unable to locate target basic block at %v from %v in function at %v", target, block.Addr, f.Addr)
    49  			}
    50  			e := &Edge{
    51  				Edge: simple.Edge{
    52  					F: from,
    53  					T: to,
    54  				},
    55  				Attrs: make(Attrs),
    56  			}
    57  			if len(targets) == 2 {
    58  				switch i {
    59  				case 0:
    60  					// true branch.
    61  					e.Attrs["label"] = "true"
    62  					e.Attrs["color"] = "darkgreen"
    63  				case 1:
    64  					// false branch.
    65  					e.Attrs["label"] = "false"
    66  					e.Attrs["color"] = "red"
    67  				}
    68  			}
    69  			g.SetEdge(e)
    70  		}
    71  	}
    72  	return g, nil
    73  }
    74  
    75  // Node is a basic block of a function.
    76  type Node struct {
    77  	graph.Node
    78  	// DOTID of node.
    79  	id string
    80  	// DOT attributes.
    81  	Attrs
    82  }
    83  
    84  // DOTID returns the DOTID of the node.
    85  func (n Node) DOTID() string {
    86  	return n.id
    87  }
    88  
    89  // Edge is an edge between two basic blocks in a function.
    90  type Edge struct {
    91  	graph.Edge
    92  	// DOT attributes.
    93  	Attrs
    94  }
    95  
    96  // ### [ Helper functions ] ####################################################
    97  
    98  // Attrs specifies a set of DOT attributes as key-value pairs.
    99  type Attrs map[string]string
   100  
   101  // --- [ encoding.Attributer ] -------------------------------------------------
   102  
   103  // Attributes returns the DOT attributes of a node or edge.
   104  func (a Attrs) Attributes() []encoding.Attribute {
   105  	var keys []string
   106  	for key := range a {
   107  		keys = append(keys, key)
   108  	}
   109  	sort.Strings(keys)
   110  	var attrs []encoding.Attribute
   111  	for _, key := range keys {
   112  		attr := encoding.Attribute{
   113  			Key:   key,
   114  			Value: a[key],
   115  		}
   116  		attrs = append(attrs, attr)
   117  	}
   118  	return attrs
   119  }