Example #1
0
// 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
}
Example #2
0
// 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)
}
Example #3
0
// 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
}