github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/cover/backend/dwarf.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 "bufio" 8 "bytes" 9 "debug/dwarf" 10 "debug/elf" 11 "encoding/binary" 12 "fmt" 13 "io" 14 "path/filepath" 15 "regexp" 16 "runtime" 17 "sort" 18 "strconv" 19 "strings" 20 21 "github.com/google/syzkaller/pkg/osutil" 22 "github.com/google/syzkaller/pkg/symbolizer" 23 "github.com/google/syzkaller/sys/targets" 24 ) 25 26 type dwarfParams struct { 27 target *targets.Target 28 objDir string 29 srcDir string 30 buildDir string 31 splitBuildDelimiters []string 32 moduleObj []string 33 hostModules []KernelModule 34 readSymbols func(*Module, *symbolInfo) ([]*Symbol, error) 35 readTextData func(*Module) ([]byte, error) 36 readModuleCoverPoints func(*targets.Target, *Module, *symbolInfo) ([2][]uint64, error) 37 readTextRanges func(*Module) ([]pcRange, []*CompileUnit, error) 38 getCompilerVersion func(string) string 39 } 40 41 type Arch struct { 42 scanSize int 43 callLen int 44 relaOffset uint64 45 callRelocType uint64 46 isCallInsn func(arch *Arch, insn []byte) bool 47 callTarget func(arch *Arch, insn []byte, pc uint64) uint64 48 } 49 50 var arches = map[string]Arch{ 51 targets.AMD64: { 52 scanSize: 1, 53 callLen: 5, 54 relaOffset: 1, 55 callRelocType: uint64(elf.R_X86_64_PLT32), 56 isCallInsn: func(arch *Arch, insn []byte) bool { 57 return insn[0] == 0xe8 58 }, 59 callTarget: func(arch *Arch, insn []byte, pc uint64) uint64 { 60 off := uint64(int64(int32(binary.LittleEndian.Uint32(insn[1:])))) 61 return pc + off + uint64(arch.callLen) 62 }, 63 }, 64 targets.ARM64: { 65 scanSize: 4, 66 callLen: 4, 67 callRelocType: uint64(elf.R_AARCH64_CALL26), 68 isCallInsn: func(arch *Arch, insn []byte) bool { 69 const mask = uint32(0xfc000000) 70 const opc = uint32(0x94000000) 71 return binary.LittleEndian.Uint32(insn)&mask == opc 72 }, 73 callTarget: func(arch *Arch, insn []byte, pc uint64) uint64 { 74 off26 := binary.LittleEndian.Uint32(insn) & 0x3ffffff 75 sign := off26 >> 25 76 off := uint64(off26) 77 // Sign-extend the 26-bit offset stored in the instruction. 78 if sign == 1 { 79 off |= 0xfffffffffc000000 80 } 81 return pc + 4*off 82 }, 83 }, 84 } 85 86 func makeDWARF(params *dwarfParams) (impl *Impl, err error) { 87 defer func() { 88 // It turns out that the DWARF-parsing library in Go crashes while parsing DWARF 5 data. 89 // As GCC11 uses DWARF 5 by default, we can expect larger number of problems with 90 // syzkallers compiled using old go versions. 91 // So we just catch the panic and turn it into a meaningful error message. 92 if recErr := recover(); recErr != nil { 93 impl = nil 94 err = fmt.Errorf("panic occurred while parsing DWARF "+ 95 "(possible remedy: use go1.16+ which support DWARF 5 debug data): %s", recErr) 96 } 97 }() 98 impl, err = makeDWARFUnsafe(params) 99 return 100 } 101 102 type Result struct { 103 CoverPoints [2][]uint64 104 Symbols []*Symbol 105 } 106 107 func processModule(params *dwarfParams, module *Module, info *symbolInfo, 108 target *targets.Target) (*Result, error) { 109 symbols, err := params.readSymbols(module, info) 110 if err != nil { 111 return nil, err 112 } 113 114 var data []byte 115 var coverPoints [2][]uint64 116 if target.Arch != targets.AMD64 && target.Arch != targets.ARM64 { 117 coverPoints, err = objdump(target, module) 118 } else if module.Name == "" { 119 data, err = params.readTextData(module) 120 if err != nil { 121 return nil, err 122 } 123 coverPoints, err = readCoverPoints(target, info, data) 124 } else { 125 coverPoints, err = params.readModuleCoverPoints(target, module, info) 126 } 127 if err != nil { 128 return nil, err 129 } 130 131 result := &Result{ 132 Symbols: symbols, 133 CoverPoints: coverPoints, 134 } 135 return result, nil 136 } 137 138 func makeDWARFUnsafe(params *dwarfParams) (*Impl, error) { 139 target := params.target 140 objDir := params.objDir 141 srcDir := params.srcDir 142 buildDir := params.buildDir 143 splitBuildDelimiters := params.splitBuildDelimiters 144 modules, err := discoverModules(target, objDir, params.moduleObj, params.hostModules) 145 if err != nil { 146 return nil, err 147 } 148 149 // Here and below index 0 refers to coverage callbacks (__sanitizer_cov_trace_pc(_guard)) 150 // and index 1 refers to comparison callbacks (__sanitizer_cov_trace_cmp*). 151 var allCoverPoints [2][]uint64 152 var allSymbols []*Symbol 153 var allRanges []pcRange 154 var allUnits []*CompileUnit 155 var pcBase uint64 156 preciseCoverage := true 157 for _, module := range modules { 158 errc := make(chan error, 1) 159 go func() { 160 info := &symbolInfo{ 161 tracePC: make(map[uint64]bool), 162 traceCmp: make(map[uint64]bool), 163 tracePCIdx: make(map[int]bool), 164 traceCmpIdx: make(map[int]bool), 165 } 166 result, err := processModule(params, module, info, target) 167 if err != nil { 168 errc <- err 169 return 170 } 171 allSymbols = append(allSymbols, result.Symbols...) 172 if module.Name == "" { 173 pcBase = info.textAddr 174 } 175 allCoverPoints[0] = append(allCoverPoints[0], result.CoverPoints[0]...) 176 allCoverPoints[1] = append(allCoverPoints[1], result.CoverPoints[1]...) 177 if module.Name == "" && len(result.CoverPoints[0]) == 0 { 178 err = fmt.Errorf("%v doesn't contain coverage callbacks (set CONFIG_KCOV=y on linux)", module.Path) 179 } 180 errc <- err 181 }() 182 ranges, units, err := params.readTextRanges(module) 183 if err != nil { 184 return nil, err 185 } 186 if err := <-errc; err != nil { 187 return nil, err 188 } 189 allRanges = append(allRanges, ranges...) 190 allUnits = append(allUnits, units...) 191 if isKcovBrokenInCompiler(params.getCompilerVersion(module.Path)) { 192 preciseCoverage = false 193 } 194 } 195 196 sort.Slice(allSymbols, func(i, j int) bool { 197 return allSymbols[i].Start < allSymbols[j].Start 198 }) 199 sort.Slice(allRanges, func(i, j int) bool { 200 return allRanges[i].start < allRanges[j].start 201 }) 202 for k := range allCoverPoints { 203 sort.Slice(allCoverPoints[k], func(i, j int) bool { 204 return allCoverPoints[k][i] < allCoverPoints[k][j] 205 }) 206 } 207 208 allSymbols = buildSymbols(allSymbols, allRanges, allCoverPoints) 209 nunit := 0 210 for _, unit := range allUnits { 211 if len(unit.PCs) == 0 { 212 continue // drop the unit 213 } 214 // TODO: objDir won't work for out-of-tree modules. 215 unit.Name, unit.Path = cleanPath(unit.Name, objDir, srcDir, buildDir, splitBuildDelimiters) 216 allUnits[nunit] = unit 217 nunit++ 218 } 219 allUnits = allUnits[:nunit] 220 if len(allSymbols) == 0 || len(allUnits) == 0 { 221 return nil, fmt.Errorf("failed to parse DWARF (set CONFIG_DEBUG_INFO=y on linux)") 222 } 223 if target.OS == targets.FreeBSD { 224 // On FreeBSD .text address in ELF is 0, but .text is actually mapped at 0xffffffff. 225 pcBase = ^uint64(0) 226 } 227 var interner symbolizer.Interner 228 impl := &Impl{ 229 Units: allUnits, 230 Symbols: allSymbols, 231 Symbolize: func(pcs map[*Module][]uint64) ([]Frame, error) { 232 return symbolize(target, &interner, objDir, srcDir, buildDir, splitBuildDelimiters, pcs) 233 }, 234 RestorePC: makeRestorePC(params, pcBase), 235 CallbackPoints: allCoverPoints[0], 236 PreciseCoverage: preciseCoverage, 237 } 238 return impl, nil 239 } 240 241 func makeRestorePC(params *dwarfParams, pcBase uint64) func(pc uint32) uint64 { 242 return func(pcLow uint32) uint64 { 243 return PreviousInstructionPC(params.target, RestorePC(pcLow, uint32(pcBase>>32))) 244 } 245 } 246 247 func buildSymbols(symbols []*Symbol, ranges []pcRange, coverPoints [2][]uint64) []*Symbol { 248 // Assign coverage point PCs to symbols. 249 // Both symbols and coverage points are sorted, so we do it one pass over both. 250 selectPCs := func(u *ObjectUnit, typ int) *[]uint64 { 251 return [2]*[]uint64{&u.PCs, &u.CMPs}[typ] 252 } 253 for pcType := range coverPoints { 254 pcs := coverPoints[pcType] 255 var curSymbol *Symbol 256 firstSymbolPC, symbolIdx := -1, 0 257 for i := 0; i < len(pcs); i++ { 258 pc := pcs[i] 259 for ; symbolIdx < len(symbols) && pc >= symbols[symbolIdx].End; symbolIdx++ { 260 } 261 var symb *Symbol 262 if symbolIdx < len(symbols) && pc >= symbols[symbolIdx].Start && pc < symbols[symbolIdx].End { 263 symb = symbols[symbolIdx] 264 } 265 if curSymbol != nil && curSymbol != symb { 266 *selectPCs(&curSymbol.ObjectUnit, pcType) = pcs[firstSymbolPC:i] 267 firstSymbolPC = -1 268 } 269 curSymbol = symb 270 if symb != nil && firstSymbolPC == -1 { 271 firstSymbolPC = i 272 } 273 } 274 if curSymbol != nil { 275 *selectPCs(&curSymbol.ObjectUnit, pcType) = pcs[firstSymbolPC:] 276 } 277 } 278 // Assign compile units to symbols based on unit pc ranges. 279 // Do it one pass as both are sorted. 280 nsymbol := 0 281 rangeIndex := 0 282 for _, s := range symbols { 283 for ; rangeIndex < len(ranges) && ranges[rangeIndex].end <= s.Start; rangeIndex++ { 284 } 285 if rangeIndex == len(ranges) || s.Start < ranges[rangeIndex].start || len(s.PCs) == 0 { 286 continue // drop the symbol 287 } 288 unit := ranges[rangeIndex].unit 289 s.Unit = unit 290 symbols[nsymbol] = s 291 nsymbol++ 292 } 293 symbols = symbols[:nsymbol] 294 295 for pcType := range coverPoints { 296 for _, s := range symbols { 297 symbPCs := selectPCs(&s.ObjectUnit, pcType) 298 unitPCs := selectPCs(&s.Unit.ObjectUnit, pcType) 299 pos := len(*unitPCs) 300 *unitPCs = append(*unitPCs, *symbPCs...) 301 *symbPCs = (*unitPCs)[pos:] 302 } 303 } 304 return symbols 305 } 306 307 // Regexps to parse compiler version string in isKcovBrokenInCompiler. 308 // Some targets (e.g. NetBSD) use g++ instead of gcc. 309 var gccRE = regexp.MustCompile(`gcc|GCC|g\+\+`) 310 var gccVersionRE = regexp.MustCompile(`(gcc|GCC|g\+\+).* ([0-9]{1,2})\.[0-9]+\.[0-9]+`) 311 312 // GCC < 14 incorrectly tail-calls kcov callbacks, which does not let syzkaller 313 // verify that collected coverage points have matching callbacks. 314 // See https://github.com/google/syzkaller/issues/4447 for more information. 315 func isKcovBrokenInCompiler(versionStr string) bool { 316 if !gccRE.MatchString(versionStr) { 317 return false 318 } 319 groups := gccVersionRE.FindStringSubmatch(versionStr) 320 if len(groups) > 0 { 321 version, err := strconv.Atoi(groups[2]) 322 if err == nil { 323 return version < 14 324 } 325 } 326 return true 327 } 328 329 type symbolInfo struct { 330 textAddr uint64 331 // Set of addresses that correspond to __sanitizer_cov_trace_pc or its trampolines. 332 tracePC map[uint64]bool 333 traceCmp map[uint64]bool 334 tracePCIdx map[int]bool 335 traceCmpIdx map[int]bool 336 } 337 338 type pcRange struct { 339 start uint64 340 end uint64 341 unit *CompileUnit 342 } 343 344 type pcFixFn = (func([2]uint64) ([2]uint64, bool)) 345 346 func readTextRanges(debugInfo *dwarf.Data, module *Module, pcFix pcFixFn) ( 347 []pcRange, []*CompileUnit, error) { 348 var ranges []pcRange 349 var units []*CompileUnit 350 for r := debugInfo.Reader(); ; { 351 ent, err := r.Next() 352 if err != nil { 353 return nil, nil, err 354 } 355 if ent == nil { 356 break 357 } 358 if ent.Tag != dwarf.TagCompileUnit { 359 return nil, nil, fmt.Errorf("found unexpected tag %v on top level", ent.Tag) 360 } 361 attrName := ent.Val(dwarf.AttrName) 362 if attrName == nil { 363 continue 364 } 365 unit := &CompileUnit{ 366 ObjectUnit: ObjectUnit{ 367 Name: attrName.(string), 368 }, 369 Module: module, 370 } 371 units = append(units, unit) 372 ranges1, err := debugInfo.Ranges(ent) 373 if err != nil { 374 return nil, nil, err 375 } 376 377 var filtered bool 378 for _, r := range ranges1 { 379 if pcFix != nil { 380 r, filtered = pcFix(r) 381 if filtered { 382 continue 383 } 384 } 385 ranges = append(ranges, pcRange{r[0] + module.Addr, r[1] + module.Addr, unit}) 386 } 387 r.SkipChildren() 388 } 389 return ranges, units, nil 390 } 391 392 func symbolizeModule(target *targets.Target, interner *symbolizer.Interner, objDir, srcDir, buildDir string, 393 splitBuildDelimiters []string, mod *Module, pcs []uint64) ([]Frame, error) { 394 procs := runtime.GOMAXPROCS(0) / 2 395 if need := len(pcs) / 1000; procs > need { 396 procs = need 397 } 398 const ( 399 minProcs = 1 400 maxProcs = 4 401 ) 402 // addr2line on a beefy vmlinux takes up to 1.6GB of RAM, so don't create too many of them. 403 if procs > maxProcs { 404 procs = maxProcs 405 } 406 if procs < minProcs { 407 procs = minProcs 408 } 409 type symbolizerResult struct { 410 frames []symbolizer.Frame 411 err error 412 } 413 symbolizerC := make(chan symbolizerResult, procs) 414 pcchan := make(chan []uint64, procs) 415 for p := 0; p < procs; p++ { 416 go func() { 417 symb := symbolizer.NewSymbolizer(target) 418 defer symb.Close() 419 var res symbolizerResult 420 for pcs := range pcchan { 421 for i, pc := range pcs { 422 pcs[i] = pc - mod.Addr 423 } 424 frames, err := symb.SymbolizeArray(mod.Path, pcs) 425 if err != nil { 426 res.err = fmt.Errorf("failed to symbolize: %w", err) 427 } 428 res.frames = append(res.frames, frames...) 429 } 430 symbolizerC <- res 431 }() 432 } 433 for i := 0; i < len(pcs); { 434 end := i + 100 435 if end > len(pcs) { 436 end = len(pcs) 437 } 438 pcchan <- pcs[i:end] 439 i = end 440 } 441 close(pcchan) 442 var err0 error 443 var frames []Frame 444 for p := 0; p < procs; p++ { 445 res := <-symbolizerC 446 if res.err != nil { 447 err0 = res.err 448 } 449 for _, frame := range res.frames { 450 name, path := cleanPath(frame.File, objDir, srcDir, buildDir, splitBuildDelimiters) 451 frames = append(frames, Frame{ 452 Module: mod, 453 PC: frame.PC + mod.Addr, 454 Name: interner.Do(name), 455 FuncName: frame.Func, 456 Path: interner.Do(path), 457 Inline: frame.Inline, 458 Range: Range{ 459 StartLine: frame.Line, 460 StartCol: 0, 461 EndLine: frame.Line, 462 EndCol: LineEnd, 463 }, 464 }) 465 } 466 } 467 if err0 != nil { 468 return nil, err0 469 } 470 return frames, nil 471 } 472 473 func symbolize(target *targets.Target, interner *symbolizer.Interner, objDir, srcDir, buildDir string, 474 splitBuildDelimiters []string, pcs map[*Module][]uint64) ([]Frame, error) { 475 var frames []Frame 476 for mod, pcs1 := range pcs { 477 frames1, err := symbolizeModule(target, interner, objDir, srcDir, buildDir, splitBuildDelimiters, mod, pcs1) 478 if err != nil { 479 return nil, err 480 } 481 frames = append(frames, frames1...) 482 } 483 return frames, nil 484 } 485 486 // nextCallTarget finds the next call instruction in data[] starting at *pos and returns that 487 // instruction's target and pc. 488 func nextCallTarget(arch *Arch, textAddr uint64, data []byte, pos *int) (uint64, uint64) { 489 for *pos < len(data) { 490 i := *pos 491 if i+arch.callLen > len(data) { 492 break 493 } 494 *pos += arch.scanSize 495 insn := data[i : i+arch.callLen] 496 if !arch.isCallInsn(arch, insn) { 497 continue 498 } 499 pc := textAddr + uint64(i) 500 callTarget := arch.callTarget(arch, insn, pc) 501 *pos = i + arch.scanSize 502 return callTarget, pc 503 } 504 return 0, 0 505 } 506 507 // readCoverPoints finds all coverage points (calls of __sanitizer_cov_trace_*) in the object file. 508 // Currently it is [amd64|arm64]-specific: looks for opcode and correct offset. 509 // Running objdump on the whole object file is too slow. 510 func readCoverPoints(target *targets.Target, info *symbolInfo, data []byte) ([2][]uint64, error) { 511 var pcs [2][]uint64 512 if len(info.tracePC) == 0 { 513 return pcs, fmt.Errorf("no __sanitizer_cov_trace_pc symbol in the object file") 514 } 515 516 i := 0 517 for { 518 arch := arches[target.Arch] 519 callTarget, pc := nextCallTarget(&arch, info.textAddr, data, &i) 520 if callTarget == 0 { 521 break 522 } 523 if info.tracePC[callTarget] { 524 pcs[0] = append(pcs[0], pc) 525 } else if info.traceCmp[callTarget] { 526 pcs[1] = append(pcs[1], pc) 527 } 528 } 529 return pcs, nil 530 } 531 532 // Source files for Android may be split between two subdirectories: the common AOSP kernel 533 // and the device-specific drivers: https://source.android.com/docs/setup/build/building-pixel-kernels. 534 // Android build system references these subdirectories in various ways, which often results in 535 // paths to non-existent files being recorded in the debug info. 536 // 537 // cleanPathAndroid() assumes that the subdirectories reside in `srcDir`, with their names being listed in 538 // `delimiters`. 539 // If one of the `delimiters` occurs in the `path`, it is stripped together with the path prefix, and the 540 // remaining file path is appended to `srcDir + delimiter`. 541 // If none of the `delimiters` occur in the `path`, `path` is treated as a relative path that needs to be 542 // looked up in `srcDir + delimiters[i]`. 543 func cleanPathAndroid(path, srcDir string, delimiters []string, existFn func(string) bool) (string, string) { 544 if len(delimiters) == 0 { 545 return "", "" 546 } 547 reStr := "(" + strings.Join(delimiters, "|") + ")(.*)" 548 re := regexp.MustCompile(reStr) 549 match := re.FindStringSubmatch(path) 550 if match != nil { 551 delimiter := match[1] 552 filename := match[2] 553 path := filepath.Clean(srcDir + delimiter + filename) 554 return filename, path 555 } 556 // None of the delimiters found in `path`: it is probably a relative path to the source file. 557 // Try to look it up in every subdirectory of srcDir. 558 for _, delimiter := range delimiters { 559 absPath := filepath.Clean(srcDir + delimiter + path) 560 if existFn(absPath) { 561 return path, absPath 562 } 563 } 564 return "", "" 565 } 566 567 func cleanPath(path, objDir, srcDir, buildDir string, splitBuildDelimiters []string) (string, string) { 568 filename := "" 569 570 path = filepath.Clean(path) 571 aname, apath := cleanPathAndroid(path, srcDir, splitBuildDelimiters, osutil.IsExist) 572 if aname != "" { 573 return aname, apath 574 } 575 absPath := osutil.Abs(path) 576 switch { 577 case strings.HasPrefix(absPath, objDir): 578 // Assume the file was built there. 579 path = strings.TrimPrefix(absPath, objDir) 580 filename = filepath.Join(objDir, path) 581 case strings.HasPrefix(absPath, buildDir): 582 // Assume the file was moved from buildDir to srcDir. 583 path = strings.TrimPrefix(absPath, buildDir) 584 filename = filepath.Join(srcDir, path) 585 default: 586 // Assume this is relative path. 587 filename = filepath.Join(srcDir, path) 588 } 589 return strings.TrimLeft(filepath.Clean(path), "/\\"), filename 590 } 591 592 // objdump is an old, slow way of finding coverage points. 593 // amd64 uses faster option of parsing binary directly (readCoverPoints). 594 // TODO: use the faster approach for all other arches and drop this. 595 func objdump(target *targets.Target, mod *Module) ([2][]uint64, error) { 596 var pcs [2][]uint64 597 cmd := osutil.Command(target.Objdump, "-d", "--no-show-raw-insn", mod.Path) 598 stdout, err := cmd.StdoutPipe() 599 if err != nil { 600 return pcs, err 601 } 602 defer stdout.Close() 603 stderr, err := cmd.StderrPipe() 604 if err != nil { 605 return pcs, err 606 } 607 defer stderr.Close() 608 if err := cmd.Start(); err != nil { 609 return pcs, fmt.Errorf("failed to run objdump on %v: %w", mod.Path, err) 610 } 611 defer func() { 612 cmd.Process.Kill() 613 cmd.Wait() 614 }() 615 s := bufio.NewScanner(stdout) 616 callInsns, traceFuncs := archCallInsn(target) 617 for s.Scan() { 618 if pc := parseLine(callInsns, traceFuncs, s.Bytes()); pc != 0 { 619 pcs[0] = append(pcs[0], pc+mod.Addr) 620 } 621 } 622 stderrOut, _ := io.ReadAll(stderr) 623 if err := cmd.Wait(); err != nil { 624 return pcs, fmt.Errorf("failed to run objdump on %v: %w\n%s", mod.Path, err, stderrOut) 625 } 626 if err := s.Err(); err != nil { 627 return pcs, fmt.Errorf("failed to run objdump on %v: %w\n%s", mod.Path, err, stderrOut) 628 } 629 return pcs, nil 630 } 631 632 func parseLine(callInsns, traceFuncs [][]byte, ln []byte) uint64 { 633 pos := -1 634 for _, callInsn := range callInsns { 635 if pos = bytes.Index(ln, callInsn); pos != -1 { 636 break 637 } 638 } 639 if pos == -1 { 640 return 0 641 } 642 hasCall := false 643 for _, traceFunc := range traceFuncs { 644 if hasCall = bytes.Contains(ln[pos:], traceFunc); hasCall { 645 break 646 } 647 } 648 if !hasCall { 649 return 0 650 } 651 for len(ln) != 0 && ln[0] == ' ' { 652 ln = ln[1:] 653 } 654 colon := bytes.IndexByte(ln, ':') 655 if colon == -1 { 656 return 0 657 } 658 pc, err := strconv.ParseUint(string(ln[:colon]), 16, 64) 659 if err != nil { 660 return 0 661 } 662 return pc 663 } 664 665 func archCallInsn(target *targets.Target) ([][]byte, [][]byte) { 666 callName := [][]byte{[]byte(" <__sanitizer_cov_trace_pc>")} 667 switch target.Arch { 668 case targets.I386: 669 // c1000102: call c10001f0 <__sanitizer_cov_trace_pc> 670 return [][]byte{[]byte("\tcall ")}, callName 671 case targets.ARM64: 672 // ffff0000080d9cc0: bl ffff00000820f478 <__sanitizer_cov_trace_pc> 673 return [][]byte{[]byte("\tbl ")}, [][]byte{ 674 []byte("<__sanitizer_cov_trace_pc>"), 675 []byte("<____sanitizer_cov_trace_pc_veneer>"), 676 } 677 678 case targets.ARM: 679 // 8010252c: bl 801c3280 <__sanitizer_cov_trace_pc> 680 return [][]byte{[]byte("\tbl\t")}, callName 681 case targets.PPC64LE: 682 // c00000000006d904: bl c000000000350780 <.__sanitizer_cov_trace_pc> 683 // This is only known to occur in the test: 684 // 838: bl 824 <__sanitizer_cov_trace_pc+0x8> 685 // This occurs on PPC64LE: 686 // c0000000001c21a8: bl c0000000002df4a0 <__sanitizer_cov_trace_pc> 687 return [][]byte{[]byte("\tbl ")}, [][]byte{ 688 []byte("<__sanitizer_cov_trace_pc>"), 689 []byte("<__sanitizer_cov_trace_pc+0x8>"), 690 []byte(" <.__sanitizer_cov_trace_pc>"), 691 } 692 case targets.MIPS64LE: 693 // ffffffff80100420: jal ffffffff80205880 <__sanitizer_cov_trace_pc> 694 // This is only known to occur in the test: 695 // b58: bal b30 <__sanitizer_cov_trace_pc> 696 return [][]byte{[]byte("\tjal\t"), []byte("\tbal\t")}, callName 697 case targets.S390x: 698 // 1001de: brasl %r14,2bc090 <__sanitizer_cov_trace_pc> 699 return [][]byte{[]byte("\tbrasl\t")}, callName 700 case targets.RiscV64: 701 // ffffffe000200018: jal ra,ffffffe0002935b0 <__sanitizer_cov_trace_pc> 702 // ffffffe0000010da: jalr 1242(ra) # ffffffe0002935b0 <__sanitizer_cov_trace_pc> 703 return [][]byte{[]byte("\tjal\t"), []byte("\tjalr\t")}, callName 704 default: 705 panic(fmt.Sprintf("unknown arch %q", target.Arch)) 706 } 707 }