github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/build/android.go (about) 1 // Copyright 2023 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 build 5 6 import ( 7 "archive/tar" 8 "fmt" 9 "io" 10 "os" 11 "path/filepath" 12 "regexp" 13 "strings" 14 "time" 15 16 "github.com/google/syzkaller/pkg/config" 17 "github.com/google/syzkaller/pkg/osutil" 18 ) 19 20 type android struct{} 21 22 type BuildParams struct { 23 BuildScript string `json:"build_script"` 24 EnvVars []string `json:"env_vars"` 25 Flags []string `json:"flags"` 26 AdditionalImages []string `json:"additional_images"` 27 AutoconfPath string `json:"autoconf_path"` 28 ConfigPath string `json:"config_path"` 29 } 30 31 var ccCompilerRegexp = regexp.MustCompile(`#define\s+CONFIG_CC_VERSION_TEXT\s+"(.*)"`) 32 33 func parseConfig(conf []byte) (*BuildParams, error) { 34 buildCfg := new(BuildParams) 35 if err := config.LoadData(conf, buildCfg); err != nil { 36 return nil, fmt.Errorf("failed to parse build config: %w", err) 37 } 38 39 if buildCfg.BuildScript == "" { 40 return nil, fmt.Errorf("build script not specified for Android build") 41 } 42 43 if buildCfg.ConfigPath == "" { 44 return nil, fmt.Errorf("kernel config path not specified for Android build") 45 } 46 47 return buildCfg, nil 48 } 49 50 func (a android) readCompiler(path string) (string, error) { 51 bytes, err := os.ReadFile(path) 52 if err != nil { 53 return "", err 54 } 55 result := ccCompilerRegexp.FindSubmatch(bytes) 56 if result == nil { 57 return "", fmt.Errorf("%s does not contain build information", path) 58 } 59 return string(result[1]), nil 60 } 61 62 func (a android) build(params Params) (ImageDetails, error) { 63 var details ImageDetails 64 if params.CmdlineFile != "" { 65 return details, fmt.Errorf("cmdline file is not supported for android images") 66 } 67 if params.SysctlFile != "" { 68 return details, fmt.Errorf("sysctl file is not supported for android images") 69 } 70 71 buildCfg, err := parseConfig(params.Build) 72 if err != nil { 73 return details, fmt.Errorf("error parsing android configs: %w", err) 74 } 75 76 // Build kernel. 77 cmd := osutil.Command(fmt.Sprintf("./%v", buildCfg.BuildScript), buildCfg.Flags...) 78 cmd.Dir = params.KernelDir 79 cmd.Env = append(cmd.Env, buildCfg.EnvVars...) 80 81 if _, err := osutil.Run(time.Hour, cmd); err != nil { 82 return details, fmt.Errorf("failed to build kernel: %w", err) 83 } 84 85 buildDistDir := filepath.Join(params.KernelDir, "dist") 86 87 vmlinux := filepath.Join(buildDistDir, "vmlinux") 88 89 if buildCfg.AutoconfPath != "" { 90 details.CompilerID, err = a.readCompiler(filepath.Join(params.KernelDir, buildCfg.AutoconfPath)) 91 if err != nil { 92 return details, fmt.Errorf("failed to read compiler: %w", err) 93 } 94 } 95 96 if err := osutil.CopyFile(vmlinux, filepath.Join(params.OutputDir, "obj", "vmlinux")); err != nil { 97 return details, fmt.Errorf("failed to copy vmlinux: %w", err) 98 } 99 if err := osutil.CopyFile(filepath.Join(params.KernelDir, buildCfg.ConfigPath), 100 filepath.Join(params.OutputDir, "obj", "kernel.config")); err != nil { 101 return details, fmt.Errorf("failed to copy kernel config: %w", err) 102 } 103 104 imageFile, err := os.Create(filepath.Join(params.OutputDir, "image")) 105 if err != nil { 106 return details, fmt.Errorf("failed to create output file: %w", err) 107 } 108 defer imageFile.Close() 109 110 if err := a.copyModuleFiles(filepath.Join(params.KernelDir, "out"), params.OutputDir); err != nil { 111 return details, fmt.Errorf("failed copying module files: %w", err) 112 } 113 114 images := append(buildCfg.AdditionalImages, "boot.img") 115 if err := a.embedImages(imageFile, buildDistDir, images...); err != nil { 116 return details, fmt.Errorf("failed to embed images: %w", err) 117 } 118 119 details.Signature, err = elfBinarySignature(vmlinux, params.Tracer) 120 if err != nil { 121 return details, fmt.Errorf("failed to generate signature: %w", err) 122 } 123 124 return details, nil 125 } 126 127 func (a android) copyModuleFiles(srcDir, dstDir string) error { 128 err := filepath.Walk(srcDir, 129 func(path string, info os.FileInfo, err error) error { 130 if err != nil { 131 return fmt.Errorf("error walking out dir: %w", err) 132 } 133 // Staging directories contain stripped module object files. 134 if strings.Contains(path, "staging") { 135 return nil 136 } 137 138 if filepath.Ext(path) == ".ko" { 139 if err := osutil.CopyFile(path, filepath.Join(dstDir, info.Name())); err != nil { 140 return fmt.Errorf("error copying file: %w", err) 141 } 142 } 143 return nil 144 }) 145 if err != nil { 146 return fmt.Errorf("failed to copy module objects: %w", err) 147 } 148 return nil 149 } 150 151 func (a android) embedImages(w io.Writer, srcDir string, imageNames ...string) error { 152 tw := tar.NewWriter(w) 153 defer tw.Close() 154 155 for _, name := range imageNames { 156 path := filepath.Join(srcDir, name) 157 data, err := os.ReadFile(path) 158 if err != nil { 159 return fmt.Errorf("failed to read %q: %w", name, err) 160 } 161 162 if err := tw.WriteHeader(&tar.Header{ 163 Name: name, 164 Mode: 0600, 165 Size: int64(len(data)), 166 }); err != nil { 167 return fmt.Errorf("failed to write header for %q: %w", name, err) 168 } 169 170 if _, err := tw.Write(data); err != nil { 171 return fmt.Errorf("failed to write data for %q: %w", name, err) 172 } 173 } 174 175 if err := tw.Close(); err != nil { 176 return fmt.Errorf("close archive: %w", err) 177 } 178 179 return nil 180 } 181 182 func (a android) clean(kernelDir, targetArch string) error { 183 if err := osutil.RemoveAll(filepath.Join(kernelDir, "out")); err != nil { 184 return fmt.Errorf("failed to clean 'out' directory: %w", err) 185 } 186 if err := osutil.RemoveAll(filepath.Join(kernelDir, "dist")); err != nil { 187 return fmt.Errorf("failed to clean 'dist' directory: %w", err) 188 } 189 return nil 190 }