aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2023-11-21 15:18:50 +0100
committerAlexander Potapenko <glider@google.com>2024-02-01 11:43:56 +0000
commitd61103fc7c348a4ab85310b9a08c1f05f47a8655 (patch)
treeef1ee643cd40d4ae17d90907aecc3e500d22b1ae /pkg
parent810241190102eaf849f5744ca7eeb68ad34f01d7 (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.go11
-rw-r--r--pkg/cover/backend/dwarf.go57
-rw-r--r--pkg/cover/backend/dwarf_test.go64
-rw-r--r--pkg/cover/backend/elf.go5
-rw-r--r--pkg/cover/report.go2
-rw-r--r--pkg/mgrconfig/config.go2
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"]},