aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2024-02-15 11:25:07 +0100
committerAlexander Potapenko <glider@google.com>2024-02-15 13:49:31 +0000
commitdc1189832d0b05781d9cc73d1f1ccdedba254c5a (patch)
tree5dfc620476ceca08ea74c7a09cb7886b3ad20700 /pkg
parentfd39cf6fb0a4ea0ddc3a5fb65243ecc5c50da52a (diff)
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.
Diffstat (limited to 'pkg')
-rw-r--r--pkg/cover/backend/dwarf.go16
-rw-r--r--pkg/cover/backend/elf.go46
-rw-r--r--pkg/cover/backend/elf_test.go34
-rw-r--r--pkg/cover/backend/mach-o.go2
4 files changed, 81 insertions, 17 deletions
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