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 }`