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