github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/cover/backend/elf.go (about)

     1  // Copyright 2020 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  	"debug/elf"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"io"
    11  	"path/filepath"
    12  	"strings"
    13  
    14  	"github.com/google/syzkaller/pkg/log"
    15  	"github.com/google/syzkaller/pkg/mgrconfig"
    16  	"github.com/google/syzkaller/pkg/vminfo"
    17  	"github.com/google/syzkaller/sys/targets"
    18  )
    19  
    20  func makeELF(target *targets.Target, kernelDirs *mgrconfig.KernelDirs, splitBuildDelimiters, moduleObj []string,
    21  	hostModules []*vminfo.KernelModule) (*Impl, error) {
    22  	return makeDWARF(&dwarfParams{
    23  		target:                target,
    24  		kernelDirs:            kernelDirs,
    25  		splitBuildDelimiters:  splitBuildDelimiters,
    26  		moduleObj:             moduleObj,
    27  		hostModules:           hostModules,
    28  		readSymbols:           elfReadSymbols,
    29  		readTextData:          elfReadTextData,
    30  		readModuleCoverPoints: elfReadModuleCoverPoints,
    31  		readTextRanges:        elfReadTextRanges,
    32  		getCompilerVersion:    elfGetCompilerVersion,
    33  	})
    34  }
    35  
    36  const (
    37  	TraceCbNone int = iota
    38  	TraceCbPc
    39  	TraceCbCmp
    40  )
    41  
    42  // Normally, -fsanitize-coverage=trace-pc inserts calls to __sanitizer_cov_trace_pc() at the
    43  // beginning of every basic block. -fsanitize-coverage=trace-cmp adds calls to other functions,
    44  // like __sanitizer_cov_trace_cmp1() or __sanitizer_cov_trace_const_cmp4().
    45  //
    46  // On ARM64 there can be additional symbol names inserted by the linker. By default, BL instruction
    47  // can only target addresses within the +/-128M range from PC. To target farther addresses, the
    48  // ARM64 linker inserts so-called veneers that act as trampolines for functions. We count calls to
    49  // such veneers as normal calls to __sanitizer_cov_trace_XXX.
    50  func getTraceCallbackType(name string) int {
    51  	if name == "__sanitizer_cov_trace_pc" || name == "____sanitizer_cov_trace_pc_veneer" {
    52  		return TraceCbPc
    53  	}
    54  	if strings.HasPrefix(name, "__sanitizer_cov_trace_") ||
    55  		(strings.HasPrefix(name, "____sanitizer_cov_trace_") && strings.HasSuffix(name, "_veneer")) {
    56  		return TraceCbCmp
    57  	}
    58  	return TraceCbNone
    59  }
    60  
    61  func elfReadSymbols(module *vminfo.KernelModule, info *symbolInfo) ([]*Symbol, error) {
    62  	file, err := elf.Open(module.Path)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  	defer file.Close()
    67  	text := file.Section(".text")
    68  	if text == nil {
    69  		return nil, fmt.Errorf("no .text section in the object file")
    70  	}
    71  	allSymbols, err := file.Symbols()
    72  	if err != nil {
    73  		return nil, fmt.Errorf("failed to read ELF symbols: %w", err)
    74  	}
    75  	info.textAddr = text.Addr
    76  	var symbols []*Symbol
    77  	for i, symb := range allSymbols {
    78  		if symb.Info&0xf != uint8(elf.STT_FUNC) && symb.Info&0xf != uint8(elf.STT_NOTYPE) {
    79  			// Only save STT_FUNC, STT_NONE otherwise some symb range inside another symb range.
    80  			continue
    81  		}
    82  		text := symb.Value >= text.Addr && symb.Value+symb.Size <= text.Addr+text.Size
    83  		if text && symb.Size != 0 {
    84  			start := symb.Value
    85  			if module.Name != "" {
    86  				start += module.Addr
    87  			}
    88  			symbols = append(symbols, &Symbol{
    89  				Module: module,
    90  				ObjectUnit: ObjectUnit{
    91  					Name: symb.Name,
    92  				},
    93  				Start: start,
    94  				End:   start + symb.Size,
    95  			})
    96  		}
    97  		switch getTraceCallbackType(symb.Name) {
    98  		case TraceCbPc:
    99  			info.tracePCIdx[i] = true
   100  			if text {
   101  				info.tracePC[symb.Value] = true
   102  			}
   103  		case TraceCbCmp:
   104  			info.traceCmpIdx[i] = true
   105  			if text {
   106  				info.traceCmp[symb.Value] = true
   107  			}
   108  		}
   109  	}
   110  	return symbols, nil
   111  }
   112  
   113  func elfReadTextRanges(module *vminfo.KernelModule) ([]pcRange, []*CompileUnit, error) {
   114  	file, err := elf.Open(module.Path)
   115  	if err != nil {
   116  		return nil, nil, err
   117  	}
   118  	defer file.Close()
   119  	text := file.Section(".text")
   120  	if text == nil {
   121  		return nil, nil, fmt.Errorf("no .text section in the object file")
   122  	}
   123  	kaslr := file.Section(".rela.text") != nil
   124  	debugInfo, err := file.DWARF()
   125  	if err != nil {
   126  		if module.Name != "" {
   127  			log.Logf(0, "ignoring module %v without DEBUG_INFO", module.Name)
   128  			return nil, nil, nil
   129  		}
   130  		return nil, nil, fmt.Errorf("failed to parse DWARF: %w (set CONFIG_DEBUG_INFO=y on linux)", err)
   131  	}
   132  
   133  	var pcFix pcFixFn
   134  	if kaslr {
   135  		pcFix = func(r [2]uint64) ([2]uint64, bool) {
   136  			if r[0] >= r[1] || r[0] < text.Addr || r[1] > text.Addr+text.Size {
   137  				// Linux kernel binaries with CONFIG_RANDOMIZE_BASE=y are strange.
   138  				// .text starts at 0xffffffff81000000 and symbols point there as
   139  				// well, but PC ranges point to addresses around 0.
   140  				// So try to add text offset and retry the check.
   141  				// It's unclear if we also need some offset on top of text.Addr,
   142  				// it gives approximately correct addresses, but not necessary
   143  				// precisely correct addresses.
   144  				r[0] += text.Addr
   145  				r[1] += text.Addr
   146  				if r[0] >= r[1] || r[0] < text.Addr || r[1] > text.Addr+text.Size {
   147  					return r, true
   148  				}
   149  			}
   150  			return r, false
   151  		}
   152  	}
   153  
   154  	return readTextRanges(debugInfo, module, pcFix)
   155  }
   156  
   157  func elfReadTextData(module *vminfo.KernelModule) ([]byte, error) {
   158  	file, err := elf.Open(module.Path)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	defer file.Close()
   163  	text := file.Section(".text")
   164  	if text == nil {
   165  		return nil, fmt.Errorf("no .text section in the object file")
   166  	}
   167  	return text.Data()
   168  }
   169  
   170  func elfReadModuleCoverPoints(target *targets.Target, module *vminfo.KernelModule, info *symbolInfo) ([2][]uint64,
   171  	error) {
   172  	var pcs [2][]uint64
   173  	file, err := elf.Open(module.Path)
   174  	if err != nil {
   175  		return pcs, err
   176  	}
   177  	defer file.Close()
   178  	callRelocType := arches[target.Arch].callRelocType
   179  	relaOffset := arches[target.Arch].relaOffset
   180  	for _, s := range file.Sections {
   181  		if s.Type != elf.SHT_RELA { // nolint: misspell
   182  			continue
   183  		}
   184  		rel := new(elf.Rela64)
   185  		for r := s.Open(); ; {
   186  			if err := binary.Read(r, binary.LittleEndian, rel); err != nil {
   187  				if err == io.EOF {
   188  					break
   189  				}
   190  				return pcs, err
   191  			}
   192  			if (rel.Info & 0xffffffff) != callRelocType {
   193  				continue
   194  			}
   195  			pc := module.Addr + rel.Off - relaOffset
   196  			index := int(elf.R_SYM64(rel.Info)) - 1
   197  			if info.tracePCIdx[index] {
   198  				pcs[0] = append(pcs[0], pc)
   199  			} else if info.traceCmpIdx[index] {
   200  				pcs[1] = append(pcs[1], pc)
   201  			}
   202  		}
   203  	}
   204  	return pcs, nil
   205  }
   206  
   207  func elfGetCompilerVersion(path string) string {
   208  	file, err := elf.Open(path)
   209  	if err != nil {
   210  		return ""
   211  	}
   212  	defer file.Close()
   213  	sec := file.Section(".comment")
   214  	if sec == nil {
   215  		return ""
   216  	}
   217  	data, err := sec.Data()
   218  	if err != nil {
   219  		return ""
   220  	}
   221  	return string(data[:])
   222  }
   223  
   224  func elfReadTextSecRange(module *vminfo.KernelModule) (*SecRange, error) {
   225  	text, err := elfReadTextSec(module)
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  	r := &SecRange{
   230  		Start: text.Addr,
   231  		End:   text.Addr + text.Size,
   232  	}
   233  	return r, nil
   234  }
   235  
   236  func elfReadTextSec(module *vminfo.KernelModule) (*elf.Section, error) {
   237  	file, err := elf.Open(module.Path)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  	defer file.Close()
   242  	text := file.Section(".text")
   243  	if text == nil {
   244  		return nil, fmt.Errorf("no .text section in the object file")
   245  	}
   246  	return text, nil
   247  }
   248  
   249  func getLinuxPCBase(cfg *mgrconfig.Config) (uint64, error) {
   250  	bin := filepath.Join(cfg.KernelObj, cfg.SysTarget.KernelObject)
   251  	file, err := elf.Open(bin)
   252  	if err != nil {
   253  		return 0, err
   254  	}
   255  	defer file.Close()
   256  	allSymbols, err := file.Symbols()
   257  	if err != nil {
   258  		return 0, err
   259  	}
   260  	for _, sym := range allSymbols {
   261  		if sym.Name == "_stext" {
   262  			return sym.Value, nil
   263  		}
   264  	}
   265  	return 0, fmt.Errorf("no _stext symbol")
   266  }