// ll2dot parses the provided LLVM IR assembly file and generates a control flow // graph for each of its defined functions using one node per basic block. func ll2dot(llPath string) error { // File name and file path without extension. baseName := pathutil.FileName(llPath) basePath := pathutil.TrimExt(llPath) // Create temporary foo.bc file, e.g. // // foo.ll -> foo.bc bcPath := fmt.Sprintf("/tmp/%s.bc", baseName) cmd := exec.Command("llvm-as", "-o", bcPath, llPath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { return errutil.Err(err) } // Remove temporary foo.bc file. defer func() { err = os.Remove(bcPath) if err != nil { log.Fatalln(errutil.Err(err)) } }() // Create output directory for the control flow graphs. dotDir := basePath + "_graphs" if flagForce { // Force remove existing graph directory. err = os.RemoveAll(dotDir) if err != nil { return errutil.Err(err) } } err = os.Mkdir(dotDir, 0755) if err != nil { return errutil.Err(err) } // Parse foo.bc module, err := llvm.ParseBitcodeFile(bcPath) if err != nil { return errutil.Err(err) } defer module.Dispose() // Get function names. var funcNames []string if len(flagFuncs) > 0 { // Get function names from command line flag: // // -funcs="foo,bar" funcNames = strings.Split(flagFuncs, ",") } else { // Get all function names. for f := module.FirstFunction(); !f.IsNil(); f = llvm.NextFunction(f) { if f.IsDeclaration() { // Ignore function declarations (e.g. functions without bodies). continue } funcNames = append(funcNames, f.Name()) } } // Generate a control flow graph for each function. for _, funcName := range funcNames { // Generate control flow graph. if !flagQuiet { log.Printf("Parsing function: %q\n", funcName) } graph, err := createCFG(module, funcName) if err != nil { return errutil.Err(err) } // Store the control flow graph. // // For a source file "foo.ll" containing the functions "bar" and "baz" the // following DOT files will be created: // // foo_graphs/bar.dot // foo_graphs/baz.dot dotName := funcName + ".dot" dotPath := filepath.Join(dotDir, dotName) if !flagQuiet { log.Printf("Creating: %q\n", dotPath) } buf := []byte(graph.String()) err = ioutil.WriteFile(dotPath, buf, 0644) if err != nil { return errutil.Err(err) } // Generate an image representation of the control flow graph. if flagImage { pngName := funcName + ".png" pngPath := filepath.Join(dotDir, pngName) if !flagQuiet { log.Printf("Creating: %q\n", pngPath) } cmd := exec.Command("dot", "-Tpng", "-o", pngPath, dotPath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { return errutil.Err(err) } } } return nil }
// ll2go parses the provided LLVM IR assembly file and decompiles it to Go // source code. func ll2go(llPath string) error { // File name and file path without extension. baseName := pathutil.FileName(llPath) basePath := pathutil.TrimExt(llPath) // TODO: Create graphs in /tmp/xxx_graphs/*.dot // Create temporary foo.dot file, e.g. // // foo.ll -> foo_graphs/*.dot dotDir := basePath + "_graphs" if ok, _ := osutil.Exists(dotDir); !ok { if !flagQuiet { log.Printf("Creating control flow graphs for %q.\n", filepath.Base(llPath)) } cmd := exec.Command("ll2dot", "-q", "-funcs", flagFuncs, "-f", llPath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { return errutil.Err(err) } } // Create temporary foo.bc file, e.g. // // foo.ll -> foo.bc bcPath := fmt.Sprintf("/tmp/%s.bc", baseName) cmd := exec.Command("llvm-as", "-o", bcPath, llPath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { return errutil.Err(err) } // Remove temporary foo.bc file. defer func() { err = os.Remove(bcPath) if err != nil { log.Fatalln(errutil.Err(err)) } }() // Parse foo.bc module, err := llvm.ParseBitcodeFile(bcPath) if err != nil { return errutil.Err(err) } defer module.Dispose() // Get function names. var funcNames []string if len(flagFuncs) > 0 { // Get function names from command line flag: // // -funcs="foo,bar" funcNames = strings.Split(flagFuncs, ",") } else { // Get all function names. for llFunc := module.FirstFunction(); !llFunc.IsNil(); llFunc = llvm.NextFunction(llFunc) { if llFunc.IsDeclaration() { // Ignore function declarations (e.g. functions without bodies). continue } funcNames = append(funcNames, llFunc.Name()) } } // Locate package name. pkgName := flagPkgName if len(flagPkgName) == 0 { pkgName = baseName for _, funcName := range funcNames { if funcName == "main" { pkgName = "main" break } } } // Create foo.go. file := &ast.File{ Name: newIdent(pkgName), } // TODO: Implement support for global variables. // Parse each function. for _, funcName := range funcNames { if !flagQuiet { log.Printf("Parsing function: %q\n", funcName) } graph, err := parseCFG(basePath, funcName) if err != nil { return errutil.Err(err) } // Structure the CFG. dotDir := basePath + "_graphs" dotName := funcName + ".dot" dotPath := path.Join(dotDir, dotName) jsonName := funcName + ".json" jsonPath := path.Join(dotDir, jsonName) if ok, _ := osutil.Exists(jsonPath); !ok { cmd := exec.Command("restructure", "-o", jsonPath, dotPath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if !flagQuiet { log.Printf("Structuring function: %q\n", funcName) } err = cmd.Run() if err != nil { return errutil.Err(err) } } var hprims []*xprimitive.Primitive fr, err := os.Open(jsonPath) if err != nil { return errutil.Err(err) } defer fr.Close() dec := json.NewDecoder(fr) err = dec.Decode(&hprims) if err != nil { return errutil.Err(err) } f, err := parseFunc(graph, module, funcName, hprims) if err != nil { return errutil.Err(err) } file.Decls = append(file.Decls, f) if flagVerbose && !flagQuiet { printFunc(f) } } // Store Go source code to file. goPath := basePath + ".go" if !flagQuiet { log.Printf("Creating: %q\n", goPath) } return storeFile(goPath, file) }
// mimicry creates a git repository which mimics an image using a contribution // history of carefully crafted commit dates. It expects a 51x7 image with a // transparent background. func mimicry(imgPath string) (err error) { img, err := imgutil.ReadFile(imgPath) if err != nil { return err } // Verify image dimensions. bounds := img.Bounds() width, height := bounds.Dx(), bounds.Dy() if width != Width || height != Height { return fmt.Errorf("mimicry: invalid image dimensions; expected %dx%d, got %dx%d", Width, Height, width, height) } // Create an empty git repository. name := pathutil.FileName(imgPath) cmd := exec.Command("git", "init", name) cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { return err } // Forge a commit history based on the image. grid := new(Grid) for x := bounds.Min.X; x < bounds.Max.X; x++ { for y := bounds.Min.Y; y < bounds.Max.Y; y++ { c := img.At(x, y) if !isTrans(c) { // Create a commit with a date that represents the (x,y)-coordinate. date := coordDate(x, y) fmt.Println("date:", date) grid.Set(x, y) fmt.Println(grid) filePath := filepath.Join(name, "README") err = ioutil.WriteFile(filePath, grid.Bytes(), 0644) if err != nil { return err } cmd = exec.Command("git", "add", "README") cmd.Stderr = os.Stderr cmd.Dir = name err = cmd.Run() if err != nil { return err } message := fmt.Sprintf("readme: Add the cell at coordinate (%d, %d).", x, y) cmd = exec.Command("git", "commit", "-m", message, "--date", date.Format("2006-01-02 15:04:05")) cmd.Stderr = os.Stderr cmd.Dir = name err = cmd.Run() if err != nil { return err } } } } return nil }