github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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  	"strings"
    13  	"testing"
    14  
    15  	"github.com/google/syzkaller/pkg/osutil"
    16  	"github.com/google/syzkaller/prog"
    17  	"github.com/google/syzkaller/sys/targets"
    18  )
    19  
    20  // Build builds a C program from source src and returns name of the resulting binary.
    21  func Build(target *prog.Target, src []byte) (string, error) {
    22  	return build(target, src, "", "")
    23  }
    24  
    25  // BuildNoWarn is the same as Build, but ignores all compilation warnings.
    26  // Should not be used in tests, but may be used e.g. when we are bisecting and potentially
    27  // using an old repro with newer compiler, or a compiler that we never seen before.
    28  // In these cases it's more important to build successfully.
    29  func BuildNoWarn(target *prog.Target, src []byte) (string, error) {
    30  	return build(target, src, "", "", "-fpermissive", "-w")
    31  }
    32  
    33  // BuildExecutor builds the executor binary for tests.
    34  // rootDir must point to syzkaller root directory in slash notation.
    35  func BuildExecutor(t *testing.T, target *prog.Target, rootDir string, cflags ...string) string {
    36  	// Build w/o optimizations for tests. Tests can build lots of versions of executor in parallel,
    37  	// and on overloaded machines it can be slow. On my machine this reduces executor build time
    38  	// from ~7.5 to ~3.5 secs.
    39  	cflags = append(cflags, "-O0")
    40  	bin, err := build(target, nil, filepath.FromSlash(rootDir),
    41  		filepath.FromSlash("executor/executor.cc"), cflags...)
    42  	if err != nil {
    43  		t.Fatalf("failed to build executor: %v", err)
    44  	}
    45  	t.Cleanup(func() {
    46  		os.Remove(bin)
    47  	})
    48  	return bin
    49  }
    50  
    51  func build(target *prog.Target, src []byte, dir, file string, cflags ...string) (string, error) {
    52  	// We call the binary syz-executor because it sometimes shows in bug titles,
    53  	// and we don't want 2 different bugs for when a crash is triggered during fuzzing and during repro.
    54  	bin, err := osutil.TempFile("syz-executor")
    55  	if err != nil {
    56  		return "", err
    57  	}
    58  
    59  	flags := []string{
    60  		"-o", bin,
    61  		"-DGOOS_" + target.OS + "=1",
    62  		"-DGOARCH_" + target.Arch + "=1",
    63  		"-DHOSTGOOS_" + runtime.GOOS + "=1",
    64  	}
    65  	if file == "" {
    66  		flags = append(flags, "-x", "c", "-")
    67  	} else {
    68  		flags = append(flags, file)
    69  	}
    70  	sysTarget := targets.Get(target.OS, target.Arch)
    71  	compiler, targetCFlags := sysTarget.CCompiler, sysTarget.CFlags
    72  	if file != "" && !strings.HasSuffix(file, ".c") {
    73  		compiler, targetCFlags = sysTarget.CxxCompiler, sysTarget.CxxFlags
    74  	}
    75  	flags = append(flags, targetCFlags...)
    76  	flags = append(flags, cflags...)
    77  	if sysTarget.PtrSize == 4 {
    78  		// We do generate uint64's for syscall arguments that overflow longs on 32-bit archs.
    79  		flags = append(flags, "-Wno-overflow")
    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  }`