diff options
| author | Alexander Potapenko <glider@google.com> | 2023-11-21 15:18:50 +0100 |
|---|---|---|
| committer | Alexander Potapenko <glider@google.com> | 2024-02-01 11:43:56 +0000 |
| commit | d61103fc7c348a4ab85310b9a08c1f05f47a8655 (patch) | |
| tree | ef1ee643cd40d4ae17d90907aecc3e500d22b1ae /pkg | |
| parent | 810241190102eaf849f5744ca7eeb68ad34f01d7 (diff) | |
pkg/mgrconfig, pkg/cover: introduce the android_split_build flag
Source files for Pixel devices are split between the common AOSP kernel
(path/to/kernel/aosp) and the device-specific drivers residing in a
separate dir (path/to/kernel/private/google-modules for Android 14
and path/to/kernel/gs/google-modules for older Android versions).
See https://source.android.com/docs/setup/build/building-pixel-kernels
for details.
Android build system may reference these dirs in various ways, for which
syzkaller cannot always understand where it should look for the source.
The newly introduced android_split_build flags handles the problem by adding a
list of "delimiters" used when normalizing the kernel source paths.
If the path contains any of such delimiters, then everything preceding the last
delimiter in the path is replaced with the contents of "kernel_src" from the
manager config.
By default we only support "/aosp/" and "/private/" corresponding to
modern Android systems as delimiters.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/cover/backend/backend.go | 11 | ||||
| -rw-r--r-- | pkg/cover/backend/dwarf.go | 57 | ||||
| -rw-r--r-- | pkg/cover/backend/dwarf_test.go | 64 | ||||
| -rw-r--r-- | pkg/cover/backend/elf.go | 5 | ||||
| -rw-r--r-- | pkg/cover/report.go | 2 | ||||
| -rw-r--r-- | pkg/mgrconfig/config.go | 2 |
6 files changed, 129 insertions, 12 deletions
diff --git a/pkg/cover/backend/backend.go b/pkg/cover/backend/backend.go index 8a7f3b879..41ab06418 100644 --- a/pkg/cover/backend/backend.go +++ b/pkg/cover/backend/backend.go @@ -65,7 +65,7 @@ type Range struct { const LineEnd = 1 << 30 -func Make(target *targets.Target, vm, objDir, srcDir, buildDir string, +func Make(target *targets.Target, vm, objDir, srcDir, buildDir string, splitBuild bool, moduleObj []string, modules []host.KernelModule) (*Impl, error) { if objDir == "" { return nil, fmt.Errorf("kernel obj directory is not specified") @@ -76,5 +76,12 @@ func Make(target *targets.Target, vm, objDir, srcDir, buildDir string, if vm == "gvisor" { return makeGvisor(target, objDir, srcDir, buildDir, modules) } - return makeELF(target, objDir, srcDir, buildDir, moduleObj, modules) + var delimiters []string + if splitBuild { + // Path prefixes used by Android Pixel kernels. See + // https://source.android.com/docs/setup/build/building-pixel-kernels for more + // details. + delimiters = []string{"/aosp/", "/private/"} + } + return makeELF(target, objDir, srcDir, buildDir, delimiters, moduleObj, modules) } diff --git a/pkg/cover/backend/dwarf.go b/pkg/cover/backend/dwarf.go index 5902d6079..96fb5edf6 100644 --- a/pkg/cover/backend/dwarf.go +++ b/pkg/cover/backend/dwarf.go @@ -29,6 +29,7 @@ type dwarfParams struct { objDir string srcDir string buildDir string + splitBuildDelimiters []string moduleObj []string hostModules []host.KernelModule readSymbols func(*Module, *symbolInfo) ([]*Symbol, error) @@ -153,6 +154,7 @@ func makeDWARFUnsafe(params *dwarfParams) (*Impl, error) { objDir := params.objDir srcDir := params.srcDir buildDir := params.buildDir + splitBuildDelimiters := params.splitBuildDelimiters modules, err := discoverModules(target, objDir, params.moduleObj, params.hostModules, params.getModuleOffset) if err != nil { return nil, err @@ -223,7 +225,7 @@ func makeDWARFUnsafe(params *dwarfParams) (*Impl, error) { continue // drop the unit } // TODO: objDir won't work for out-of-tree modules. - unit.Name, unit.Path = cleanPath(unit.Name, objDir, srcDir, buildDir) + unit.Name, unit.Path = cleanPath(unit.Name, objDir, srcDir, buildDir, splitBuildDelimiters) allUnits[nunit] = unit nunit++ } @@ -247,7 +249,7 @@ func makeDWARFUnsafe(params *dwarfParams) (*Impl, error) { Units: allUnits, Symbols: allSymbols, Symbolize: func(pcs map[*Module][]uint64) ([]Frame, error) { - return symbolize(target, objDir, srcDir, buildDir, pcs) + return symbolize(target, objDir, srcDir, buildDir, splitBuildDelimiters, pcs) }, RestorePC: makeRestorePC(params, pcBase), CoverPoints: allCoverPointsMap, @@ -383,7 +385,7 @@ func readTextRanges(debugInfo *dwarf.Data, module *Module, pcFix pcFixFn) ( return ranges, units, nil } -func symbolizeModule(target *targets.Target, objDir, srcDir, buildDir string, +func symbolizeModule(target *targets.Target, objDir, srcDir, buildDir string, splitBuildDelimiters []string, mod *Module, pcs []uint64) ([]Frame, error) { procs := runtime.GOMAXPROCS(0) / 2 if need := len(pcs) / 1000; procs > need { @@ -441,7 +443,7 @@ func symbolizeModule(target *targets.Target, objDir, srcDir, buildDir string, err0 = res.err } for _, frame := range res.frames { - name, path := cleanPath(frame.File, objDir, srcDir, buildDir) + name, path := cleanPath(frame.File, objDir, srcDir, buildDir, splitBuildDelimiters) frames = append(frames, Frame{ Module: mod, PC: frame.PC + mod.Addr, @@ -463,11 +465,11 @@ func symbolizeModule(target *targets.Target, objDir, srcDir, buildDir string, return frames, nil } -func symbolize(target *targets.Target, objDir, srcDir, buildDir string, +func symbolize(target *targets.Target, objDir, srcDir, buildDir string, splitBuildDelimiters []string, pcs map[*Module][]uint64) ([]Frame, error) { var frames []Frame for mod, pcs1 := range pcs { - frames1, err := symbolizeModule(target, objDir, srcDir, buildDir, mod, pcs1) + frames1, err := symbolizeModule(target, objDir, srcDir, buildDir, splitBuildDelimiters, mod, pcs1) if err != nil { return nil, err } @@ -509,8 +511,49 @@ func readCoverPoints(target *targets.Target, info *symbolInfo, data []byte) ([2] return pcs, nil } -func cleanPath(path, objDir, srcDir, buildDir string) (string, string) { +// Source files for Android may be split between two subdirectories: the common AOSP kernel +// and the device-specific drivers: https://source.android.com/docs/setup/build/building-pixel-kernels. +// Android build system references these subdirectories in various ways, which often results in +// paths to non-existent files being recorded in the debug info. +// +// cleanPathAndroid() assumes that the subdirectories reside in `srcDir`, with their names being listed in +// `delimiters`. +// If one of the `delimiters` occurs in the `path`, it is stripped together with the path prefix, and the +// remaining file path is appended to `srcDir + delimiter`. +// If none of the `delimiters` occur in the `path`, `path` is treated as a relative path that needs to be +// looked up in `srcDir + delimiters[i]`. +func cleanPathAndroid(path, srcDir string, delimiters []string, existFn func(string) bool) (string, string) { + if len(delimiters) == 0 { + return "", "" + } + reStr := "(" + strings.Join(delimiters, "|") + ")(.*)" + re := regexp.MustCompile(reStr) + match := re.FindStringSubmatch(path) + if match != nil { + delimiter := match[1] + filename := match[2] + path := filepath.Clean(srcDir + delimiter + filename) + return filename, path + } + // None of the delimiters found in `path`: it is probably a relative path to the source file. + // Try to look it up in every subdirectory of srcDir. + for _, delimiter := range delimiters { + absPath := filepath.Clean(srcDir + delimiter + path) + if existFn(absPath) { + return path, absPath + } + } + return "", "" +} + +func cleanPath(path, objDir, srcDir, buildDir string, splitBuildDelimiters []string) (string, string) { filename := "" + + path = filepath.Clean(path) + aname, apath := cleanPathAndroid(path, srcDir, splitBuildDelimiters, osutil.IsExist) + if aname != "" { + return aname, apath + } absPath := osutil.Abs(path) switch { case strings.HasPrefix(absPath, objDir): diff --git a/pkg/cover/backend/dwarf_test.go b/pkg/cover/backend/dwarf_test.go index b82bea7db..98170fa07 100644 --- a/pkg/cover/backend/dwarf_test.go +++ b/pkg/cover/backend/dwarf_test.go @@ -38,3 +38,67 @@ func TestIsKcovBrokenInCompiler(t *testing.T) { } } } + +type CleanPathAndroidTest struct { + SrcDir string + Delimiters []string + // Each test case is a triple of {path, epath, ename}, where path is passed to cleanPathAndroid(), + // and (epath, ename) is its expected return value. + Files [][3]string + FnExists func(string) bool +} + +func TestCleanPathAndroid(t *testing.T) { + tests := []CleanPathAndroidTest{ + // Test that paths with "/aosp/" and "/private/" in them are normalized. + { + SrcDir: "/src/kernel", + Delimiters: []string{"/aosp/", "/private/"}, + Files: [][3]string{ + {"/src/kernel/aosp/mm/mmap.c", "mm/mmap.c", "/src/kernel/aosp/mm/mmap.c"}, + {"/src/kernel/out/cache/feedface/aosp/mm/mmap.c", "mm/mmap.c", "/src/kernel/aosp/mm/mmap.c"}, + {"/src/kernel/out/cache/cafebabe/private/google_modules/module/mod.c", "google_modules/module/mod.c", + "/src/kernel/private/google_modules/module/mod.c"}, + {"/some/other/path/aosp/mm/mmap.c", "mm/mmap.c", "/src/kernel/aosp/mm/mmap.c"}, + }, + FnExists: func(string) bool { return true }, + }, + // Test that for empty delimiters empty results are returned. + { + SrcDir: "/src/kernel/", + Delimiters: []string{}, + Files: [][3]string{ + {"/src/kernel/mm/mmap.c", "", ""}, + }, + FnExists: func(string) bool { return true }, + }, + // Test that for path that does not contain a delimiter the result is constructed based on FnExists(). + { + SrcDir: "/src/kernel/", + Delimiters: []string{"/aosp/", "/private/"}, + Files: [][3]string{ + {"mm/mmap.c", "mm/mmap.c", "/src/kernel/aosp/mm/mmap.c"}, + }, + FnExists: func(s string) bool { return s == "/src/kernel/aosp/mm/mmap.c" }, + }, + // Test that for path that does not contain a delimiter the result is constructed based on FnExists(). + { + SrcDir: "/src/kernel/", + Delimiters: []string{"/aosp/", "/private/"}, + Files: [][3]string{ + {"mm/mmap.c", "mm/mmap.c", "/src/kernel/private/mm/mmap.c"}, + }, + FnExists: func(s string) bool { return s != "/src/kernel/aosp/mm/mmap.c" }, + }, + } + for _, test := range tests { + for _, files := range test.Files { + path, epath, ename := files[0], files[1], files[2] + rpath, rname := cleanPathAndroid(path, test.SrcDir, test.Delimiters, test.FnExists) + if (rpath != epath) || (rname != ename) { + t.Fatalf("cleanPathAndroid(`%s`, `%s`, %v, ...) unexpectedly returned (`%s`, `%s`) instead of (`%s`, `%s`)\n", + path, test.SrcDir, test.Delimiters, rpath, rname, epath, ename) + } + } + } +} diff --git a/pkg/cover/backend/elf.go b/pkg/cover/backend/elf.go index 08d8890ce..b3846ed8a 100644 --- a/pkg/cover/backend/elf.go +++ b/pkg/cover/backend/elf.go @@ -15,13 +15,14 @@ import ( "github.com/google/syzkaller/sys/targets" ) -func makeELF(target *targets.Target, objDir, srcDir, buildDir string, - moduleObj []string, hostModules []host.KernelModule) (*Impl, error) { +func makeELF(target *targets.Target, objDir, srcDir, buildDir string, splitBuildDelimiters, moduleObj []string, + hostModules []host.KernelModule) (*Impl, error) { return makeDWARF(&dwarfParams{ target: target, objDir: objDir, srcDir: srcDir, buildDir: buildDir, + splitBuildDelimiters: splitBuildDelimiters, moduleObj: moduleObj, hostModules: hostModules, readSymbols: elfReadSymbols, diff --git a/pkg/cover/report.go b/pkg/cover/report.go index a886eb014..6bf7e0a6f 100644 --- a/pkg/cover/report.go +++ b/pkg/cover/report.go @@ -33,7 +33,7 @@ var RestorePC = backend.RestorePC func MakeReportGenerator(cfg *mgrconfig.Config, subsystem []mgrconfig.Subsystem, modules []host.KernelModule, rawCover bool) (*ReportGenerator, error) { impl, err := backend.Make(cfg.SysTarget, cfg.Type, cfg.KernelObj, - cfg.KernelSrc, cfg.KernelBuildSrc, cfg.ModuleObj, modules) + cfg.KernelSrc, cfg.KernelBuildSrc, cfg.AndroidSplitBuild, cfg.ModuleObj, modules) if err != nil { return nil, err } diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go index e40643b0e..19ba352f7 100644 --- a/pkg/mgrconfig/config.go +++ b/pkg/mgrconfig/config.go @@ -49,6 +49,8 @@ type Config struct { KernelSrc string `json:"kernel_src,omitempty"` // Location of the driectory where the kernel was built (if not set defaults to KernelSrc) KernelBuildSrc string `json:"kernel_build_src,omitempty"` + // Is the kernel built separately from the modules? (Specific to Android builds) + AndroidSplitBuild bool `json:"android_split_build"` // Kernel subsystem with paths to each subsystem // "kernel_subsystem": [ // { "name": "sound", "path": ["sound", "techpack/audio"]}, |
