github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/cover/backend/modules.go (about)

     1  // Copyright 2021 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 backend
     5  
     6  import (
     7  	"bytes"
     8  	"debug/elf"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/google/syzkaller/pkg/log"
    15  	"github.com/google/syzkaller/sys/targets"
    16  )
    17  
    18  type KernelModule struct {
    19  	Name string
    20  	Addr uint64
    21  	Size uint64
    22  }
    23  
    24  func discoverModules(target *targets.Target, objDir string, moduleObj []string,
    25  	hostModules []KernelModule) (
    26  	[]*Module, error) {
    27  	modules := []*Module{
    28  		// A dummy module representing the kernel itself.
    29  		{Path: filepath.Join(objDir, target.KernelObject)},
    30  	}
    31  	if target.OS == targets.Linux {
    32  		modules1, err := discoverModulesLinux(append([]string{objDir}, moduleObj...),
    33  			hostModules)
    34  		if err != nil {
    35  			return nil, err
    36  		}
    37  		modules = append(modules, modules1...)
    38  	} else if len(hostModules) != 0 {
    39  		return nil, fmt.Errorf("%v coverage does not support modules", target.OS)
    40  	}
    41  	return modules, nil
    42  }
    43  
    44  func discoverModulesLinux(dirs []string, hostModules []KernelModule) ([]*Module, error) {
    45  	paths, err := locateModules(dirs)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	var modules []*Module
    50  	for _, mod := range hostModules {
    51  		path := paths[mod.Name]
    52  		if path == "" {
    53  			log.Logf(0, "failed to discover module %v", mod.Name)
    54  			continue
    55  		}
    56  		log.Logf(0, "module %v -> %v", mod.Name, path)
    57  		modules = append(modules, &Module{
    58  			Name: mod.Name,
    59  			Addr: mod.Addr,
    60  			Path: path,
    61  		})
    62  	}
    63  	return modules, nil
    64  }
    65  
    66  func locateModules(dirs []string) (map[string]string, error) {
    67  	paths := make(map[string]string)
    68  	for _, dir := range dirs {
    69  		err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
    70  			if err != nil || filepath.Ext(path) != ".ko" {
    71  				return err
    72  			}
    73  			name, err := getModuleName(path)
    74  			if err != nil {
    75  				// Extracting module name involves parsing ELF and binary data,
    76  				// let's not fail on it, we still have the file name,
    77  				// which is usually the right module name.
    78  				log.Logf(0, "failed to get %v module name: %v", path, err)
    79  				name = strings.TrimSuffix(filepath.Base(path), "."+filepath.Ext(path))
    80  			}
    81  			// Order of dirs determine priority, so don't overwrite already discovered names.
    82  			if name != "" && paths[name] == "" {
    83  				paths[name] = path
    84  			}
    85  			return nil
    86  		})
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  	}
    91  	return paths, nil
    92  }
    93  
    94  func getModuleName(path string) (string, error) {
    95  	file, err := elf.Open(path)
    96  	if err != nil {
    97  		return "", err
    98  	}
    99  	defer file.Close()
   100  	section := file.Section(".modinfo")
   101  	if section == nil {
   102  		return "", fmt.Errorf("no .modinfo section")
   103  	}
   104  	data, err := section.Data()
   105  	if err != nil {
   106  		return "", fmt.Errorf("failed to read .modinfo: %w", err)
   107  	}
   108  	if name := searchModuleName(data); name != "" {
   109  		return name, nil
   110  	}
   111  	section = file.Section(".gnu.linkonce.this_module")
   112  	if section == nil {
   113  		return "", fmt.Errorf("no .gnu.linkonce.this_module section")
   114  	}
   115  	data, err = section.Data()
   116  	if err != nil {
   117  		return "", fmt.Errorf("failed to read .gnu.linkonce.this_module: %w", err)
   118  	}
   119  	return string(data), nil
   120  }
   121  
   122  func searchModuleName(data []byte) string {
   123  	data = append([]byte{0}, data...)
   124  	key := []byte("\x00name=")
   125  	pos := bytes.Index(data, key)
   126  	if pos == -1 {
   127  		return ""
   128  	}
   129  	end := bytes.IndexByte(data[pos+len(key):], 0)
   130  	if end == -1 {
   131  		return ""
   132  	}
   133  	end = pos + len(key) + end
   134  	if end > len(data) {
   135  		return ""
   136  	}
   137  	return string(data[pos+len(key) : end])
   138  }