From dc1189832d0b05781d9cc73d1f1ccdedba254c5a Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Thu, 15 Feb 2024 11:25:07 +0100 Subject: pkg/cover/backend: support veneers on ARM64 Certain ARM64 builds continued reporting errors about coverage points not matching __sanitizer_cov_trace_pc calls. It turned out such coverage originated from calls to ____sanitizer_cov_trace_pc_veneer functions that are inserted by the linker to extend the range of BL instructions (limited by +/-128M). Add support for __funcname_veneer functions to ELF to make sure this coverage is correctly attributed. --- pkg/cover/backend/dwarf.go | 16 ++++++++++----- pkg/cover/backend/elf.go | 46 ++++++++++++++++++++++++++++++++----------- pkg/cover/backend/elf_test.go | 34 ++++++++++++++++++++++++++++++++ pkg/cover/backend/mach-o.go | 2 +- 4 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 pkg/cover/backend/elf_test.go (limited to 'pkg') diff --git a/pkg/cover/backend/dwarf.go b/pkg/cover/backend/dwarf.go index 96fb5edf6..a88f737c6 100644 --- a/pkg/cover/backend/dwarf.go +++ b/pkg/cover/backend/dwarf.go @@ -172,6 +172,7 @@ func makeDWARFUnsafe(params *dwarfParams) (*Impl, error) { errc := make(chan error, 1) go func() { info := &symbolInfo{ + tracePC: make(map[uint64]bool), traceCmp: make(map[uint64]bool), tracePCIdx: make(map[int]bool), traceCmpIdx: make(map[int]bool), @@ -324,8 +325,9 @@ func buildSymbols(symbols []*Symbol, ranges []pcRange, coverPoints [2][]uint64) } type symbolInfo struct { - textAddr uint64 - tracePC uint64 + textAddr uint64 + // Set of addresses that correspond to __sanitizer_cov_trace_pc or its trampolines. + tracePC map[uint64]bool traceCmp map[uint64]bool tracePCIdx map[int]bool traceCmpIdx map[int]bool @@ -483,7 +485,7 @@ func symbolize(target *targets.Target, objDir, srcDir, buildDir string, splitBui // Running objdump on the whole object file is too slow. func readCoverPoints(target *targets.Target, info *symbolInfo, data []byte) ([2][]uint64, error) { var pcs [2][]uint64 - if info.tracePC == 0 { + if len(info.tracePC) == 0 { return pcs, fmt.Errorf("no __sanitizer_cov_trace_pc symbol in the object file") } @@ -502,7 +504,7 @@ func readCoverPoints(target *targets.Target, info *symbolInfo, data []byte) ([2] } pc := info.textAddr + uint64(i) target := arch.target(&arch, data[i:], pc, opcode) - if target == info.tracePC { + if info.tracePC[target] { pcs[0] = append(pcs[0], pc) } else if info.traceCmp[target] { pcs[1] = append(pcs[1], pc) @@ -652,7 +654,11 @@ func archCallInsn(target *targets.Target) ([][]byte, [][]byte) { return [][]byte{[]byte("\tcall ")}, callName case targets.ARM64: // ffff0000080d9cc0: bl ffff00000820f478 <__sanitizer_cov_trace_pc> - return [][]byte{[]byte("\tbl\t")}, callName + return [][]byte{[]byte("\tbl ")}, [][]byte{ + []byte("<__sanitizer_cov_trace_pc>"), + []byte("<____sanitizer_cov_trace_pc_veneer>"), + } + case targets.ARM: // 8010252c: bl 801c3280 <__sanitizer_cov_trace_pc> return [][]byte{[]byte("\tbl\t")}, callName diff --git a/pkg/cover/backend/elf.go b/pkg/cover/backend/elf.go index b3846ed8a..fbb6b4480 100644 --- a/pkg/cover/backend/elf.go +++ b/pkg/cover/backend/elf.go @@ -34,6 +34,31 @@ func makeELF(target *targets.Target, objDir, srcDir, buildDir string, splitBuild }) } +const ( + TraceCbNone int = iota + TraceCbPc + TraceCbCmp +) + +// Normally, -fsanitize-coverage=trace-pc inserts calls to __sanitizer_cov_trace_pc() at the +// beginning of every basic block. -fsanitize-coverage=trace-cmp adds calls to other functions, +// like __sanitizer_cov_trace_cmp1() or __sanitizer_cov_trace_const_cmp4(). +// +// On ARM64 there can be additional symbol names inserted by the linker. By default, BL instruction +// can only target addresses within the +/-128M range from PC. To target farther addresses, the +// ARM64 linker inserts so-called veneers that act as trampolines for functions. We count calls to +// such veneers as normal calls to __sanitizer_cov_trace_XXX. +func getTraceCallbackType(name string) int { + if name == "__sanitizer_cov_trace_pc" || name == "____sanitizer_cov_trace_pc_veneer" { + return TraceCbPc + } + if strings.HasPrefix(name, "__sanitizer_cov_trace_") || + (strings.HasPrefix(name, "____sanitizer_cov_trace_") && strings.HasSuffix(name, "_veneer")) { + return TraceCbCmp + } + return TraceCbNone +} + func elfReadSymbols(module *Module, info *symbolInfo) ([]*Symbol, error) { file, err := elf.Open(module.Path) if err != nil { @@ -69,17 +94,16 @@ func elfReadSymbols(module *Module, info *symbolInfo) ([]*Symbol, error) { End: start + symb.Size, }) } - if strings.HasPrefix(symb.Name, "__sanitizer_cov_trace_") { - if symb.Name == "__sanitizer_cov_trace_pc" { - info.tracePCIdx[i] = true - if text { - info.tracePC = symb.Value - } - } else { - info.traceCmpIdx[i] = true - if text { - info.traceCmp[symb.Value] = true - } + switch getTraceCallbackType(symb.Name) { + case TraceCbPc: + info.tracePCIdx[i] = true + if text { + info.tracePC[symb.Value] = true + } + case TraceCbCmp: + info.traceCmpIdx[i] = true + if text { + info.traceCmp[symb.Value] = true } } } diff --git a/pkg/cover/backend/elf_test.go b/pkg/cover/backend/elf_test.go new file mode 100644 index 000000000..56272ac66 --- /dev/null +++ b/pkg/cover/backend/elf_test.go @@ -0,0 +1,34 @@ +// Copyright 2024 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package backend + +import ( + "testing" +) + +func TestGetTraceCallbackType(t *testing.T) { + inputData := map[int][]string{ + TraceCbNone: { + "foobar", + "___sanitizer_cov_trace_pc", + }, + TraceCbPc: { + "__sanitizer_cov_trace_pc", + "____sanitizer_cov_trace_pc_veneer", + }, + TraceCbCmp: { + "__sanitizer_cov_trace_cmp1", + "__sanitizer_cov_trace_const_cmp4", + "____sanitizer_cov_trace_const_cmp4_veneer", + }, + } + for expected, names := range inputData { + for _, name := range names { + result := getTraceCallbackType(name) + if result != expected { + t.Fatalf("getTraceCallbackType(`%v`) unexpectedly returned %v", name, result) + } + } + } +} diff --git a/pkg/cover/backend/mach-o.go b/pkg/cover/backend/mach-o.go index 67e28c625..cfc56c431 100644 --- a/pkg/cover/backend/mach-o.go +++ b/pkg/cover/backend/mach-o.go @@ -75,7 +75,7 @@ func machoReadSymbols(module *Module, info *symbolInfo) ([]*Symbol, error) { if symb.Name == "___sanitizer_cov_trace_pc_guard" { info.tracePCIdx[i] = true if text { - info.tracePC = symb.Value + info.tracePC[symb.Value] = true } } else { info.traceCmpIdx[i] = true -- cgit mrf-deployment