aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/cover/backend/mach-o.go
blob: 37031cfded97ec183c019ec9fbb59b26ddd807c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2020 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 (
	"debug/macho"
	"fmt"
	"path/filepath"
	"sort"
	"strings"

	"github.com/google/syzkaller/pkg/mgrconfig"
	"github.com/google/syzkaller/pkg/vminfo"
	"github.com/google/syzkaller/sys/targets"
)

func makeMachO(target *targets.Target, kernelDirs *mgrconfig.KernelDirs,
	moduleObj []string, hostModules []*vminfo.KernelModule) (*Impl, error) {
	return makeDWARF(&dwarfParams{
		target:                target,
		kernelDirs:            kernelDirs,
		moduleObj:             moduleObj,
		hostModules:           hostModules,
		readSymbols:           machoReadSymbols,
		readTextData:          machoReadTextData,
		readModuleCoverPoints: machoReadModuleCoverPoints,
		readTextRanges:        machoReadTextRanges,
	})
}

func machoReadSymbols(module *vminfo.KernelModule, info *symbolInfo) ([]*Symbol, error) {
	file, err := macho.Open(module.Path)
	if err != nil {
		return nil, err
	}
	text := file.Section("__text")
	if text == nil {
		return nil, fmt.Errorf("no __text section in the object file")
	}
	if file.Symtab == nil {
		return nil, fmt.Errorf("failed to read Mach-O symbols")
	}
	info.textAddr = text.Addr

	// We don't get symbol lengths or symbol ends in Mach-O symbols. So we
	// guesstimate them by taking the next symbols beginning -1. That only
	// works after we have sorted them.
	sort.Slice(file.Symtab.Syms, func(i, j int) bool {
		return file.Symtab.Syms[i].Value < file.Symtab.Syms[j].Value
	})

	var symbols []*Symbol
	for i, symb := range file.Symtab.Syms {
		// Mach-Os doesn't contain the Symbol size like in ELF
		symbEnd := text.Addr + text.Size
		if i < len(file.Symtab.Syms)-1 {
			symbEnd = file.Symtab.Syms[i+1].Value
		}

		text := symb.Value >= text.Addr && symbEnd <= text.Addr+text.Size
		if text {
			symbStart := symb.Value + module.Addr
			symbols = append(symbols, &Symbol{
				Module: module,
				ObjectUnit: ObjectUnit{
					Name: symb.Name,
				},
				Start: symbStart,
				End:   symbEnd,
			})
		}
		if strings.HasPrefix(symb.Name, "___sanitizer_cov_trace_") {
			if symb.Name == "___sanitizer_cov_trace_pc_guard" {
				info.tracePCIdx[i] = true
				if text {
					info.tracePC[symb.Value] = true
				}
			} else {
				info.traceCmpIdx[i] = true
				if text {
					info.traceCmp[symb.Value] = true
				}
			}
		}
	}
	return symbols, nil
}

func machoReadTextRanges(module *vminfo.KernelModule) ([]pcRange, []*CompileUnit, error) {
	dir, kernel := filepath.Split(module.Path)
	dSYMPath := filepath.Join(dir, fmt.Sprintf(
		"%[1]s.dSYM/Contents/Resources/DWARF/%[1]s", kernel))
	dSYM, err := macho.Open(dSYMPath)
	if err != nil {
		return nil, nil, err
	}
	debugInfo, err := dSYM.DWARF()
	if err != nil {
		return nil, nil, fmt.Errorf("failed to parse DWARF: %w", err)
	}
	return readTextRanges(debugInfo, module, nil)
}

func machoReadTextData(module *vminfo.KernelModule) ([]byte, error) {
	file, err := macho.Open(module.Path)
	if err != nil {
		return nil, err
	}
	text := file.Section("__text")
	if text == nil {
		return nil, fmt.Errorf("no __text section in the object file")
	}
	return text.Data()
}

func machoReadModuleCoverPoints(target *targets.Target, module *vminfo.KernelModule, info *symbolInfo) ([2][]uint64,
	error) {
	// TODO: Linux/ELF supports module symbols. We should probably also do that
	// for XNU/Mach-O. To maximize code re-use we already have a lot of the
	// plumbing for module support. I think we mainly miss an equivalent to
	// discoverModules and this function at the moment.
	return [2][]uint64{}, fmt.Errorf("machoReadModuleCoverPoints not implemented")
}