github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/csource/build.go (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package csource
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"runtime"
    12  	"slices"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/google/syzkaller/pkg/osutil"
    17  	"github.com/google/syzkaller/prog"
    18  	"github.com/google/syzkaller/sys/targets"
    19  )
    20  
    21  // Build builds a C program from source src and returns name of the resulting binary.
    22  func Build(target *prog.Target, src []byte) (string, error) {
    23  	return build(target, src, "", "")
    24  }
    25  
    26  // BuildNoWarn is the same as Build, but ignores all compilation warnings.
    27  // Should not be used in tests, but may be used e.g. when we are bisecting and potentially
    28  // using an old repro with newer compiler, or a compiler that we never seen before.
    29  // In these cases it's more important to build successfully.
    30  func BuildNoWarn(target *prog.Target, src []byte) (string, error) {
    31  	return build(target, src, "", "", "-fpermissive", "-w")
    32  }
    33  
    34  // BuildExecutor builds the executor binary for tests.
    35  // rootDir must point to syzkaller root directory in slash notation.
    36  func BuildExecutor(t *testing.T, target *prog.Target, rootDir string, cflags ...string) string {
    37  	bin, err := build(target, nil, filepath.FromSlash(rootDir),
    38  		filepath.FromSlash("executor/executor.cc"), cflags...)
    39  	if err != nil {
    40  		t.Fatalf("failed to build executor: %v", err)
    41  	}
    42  	t.Cleanup(func() {
    43  		os.Remove(bin)
    44  	})
    45  	return bin
    46  }
    47  
    48  func build(target *prog.Target, src []byte, dir, file string, cflags ...string) (string, error) {
    49  	sysTarget := targets.Get(target.OS, target.Arch)
    50  	compiler := sysTarget.CCompiler
    51  	// We call the binary syz-executor because it sometimes shows in bug titles,
    52  	// and we don't want 2 different bugs for when a crash is triggered during fuzzing and during repro.
    53  	bin, err := osutil.TempFile("syz-executor")
    54  	if err != nil {
    55  		return "", err
    56  	}
    57  
    58  	flags := []string{
    59  		"-o", bin,
    60  		"-DGOOS_" + target.OS + "=1",
    61  		"-DGOARCH_" + target.Arch + "=1",
    62  		"-DHOSTGOOS_" + runtime.GOOS + "=1",
    63  	}
    64  	if file == "" {
    65  		flags = append(flags, "-x", "c", "-")
    66  	} else {
    67  		flags = append(flags, file)
    68  	}
    69  	flags = append(flags, sysTarget.CFlags...)
    70  	if sysTarget.PtrSize == 4 {
    71  		// We do generate uint64's for syscall arguments that overflow longs on 32-bit archs.
    72  		flags = append(flags, "-Wno-overflow")
    73  	}
    74  	flags = append(flags, cflags...)
    75  	if file == "" || strings.HasSuffix(file, ".c") {
    76  		// Building C source, so remove C++ flags.
    77  		flags = slices.DeleteFunc(flags, func(flag string) bool {
    78  			return strings.HasPrefix(flag, "-std=c++")
    79  		})
    80  	}
    81  	cmd := osutil.Command(compiler, flags...)
    82  	cmd.Dir = dir
    83  	if file == "" {
    84  		cmd.Stdin = bytes.NewReader(src)
    85  	}
    86  	out, err := cmd.CombinedOutput()
    87  	if err != nil {
    88  		os.Remove(bin)
    89  		if file != "" {
    90  			src, _ = os.ReadFile(file)
    91  		}
    92  		return "", fmt.Errorf("failed to build program:\n%s\n%s\ncompiler invocation: %v %v",
    93  			src, out, compiler, flags)
    94  	}
    95  	return bin, nil
    96  }
    97  
    98  // Format reformats C source using clang-format.
    99  func Format(src []byte) ([]byte, error) {
   100  	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
   101  	cmd := osutil.Command("clang-format", "-assume-filename=/src.c", "-style", style)
   102  	cmd.Stdin = bytes.NewReader(src)
   103  	cmd.Stdout = stdout
   104  	cmd.Stderr = stderr
   105  	if err := cmd.Run(); err != nil {
   106  		return src, fmt.Errorf("failed to format source: %w\n%v", err, stderr.String())
   107  	}
   108  	return stdout.Bytes(), nil
   109  }
   110  
   111  // Something acceptable for kernel developers and email-friendly.
   112  var style = `{
   113  BasedOnStyle: LLVM,
   114  IndentWidth: 2,
   115  UseTab: Never,
   116  BreakBeforeBraces: Linux,
   117  IndentCaseLabels: false,
   118  DerivePointerAlignment: false,
   119  PointerAlignment: Left,
   120  AlignTrailingComments: true,
   121  AllowShortBlocksOnASingleLine: false,
   122  AllowShortCaseLabelsOnASingleLine: false,
   123  AllowShortFunctionsOnASingleLine: false,
   124  AllowShortIfStatementsOnASingleLine: false,
   125  AllowShortLoopsOnASingleLine: false,
   126  ColumnLimit: 80,
   127  }`