-
Notifications
You must be signed in to change notification settings - Fork 67
/
maps.go
137 lines (120 loc) · 4.45 KB
/
maps.go
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
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2022-2023 The Parca Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package unwind
import (
"fmt"
"strings"
"github.com/prometheus/procfs"
)
// ExecutableMapping represents an executable memory mapping.
type ExecutableMapping struct {
LoadAddr uint64
StartAddr uint64
EndAddr uint64
Executable string
mainExec bool
}
// IsMainObject returns whether this executable is the "main executable".
// which triggered the loading of all the other mappings.
//
// We care about this because if Linux ASLR is enabled, we have to
// modify the loaded addresses for the main object.
func (pm *ExecutableMapping) IsMainObject() bool {
return pm.mainExec
}
// IsJitted returns whether an executable mapping is JITed or not.
// The detection is done by checking if the executable mapping is
// not backed by a file.
//
// We don't check for the writeable flag as `mprotect(2)` may be
// called to make it r+w only.
func (pm *ExecutableMapping) IsJitted() bool {
return pm.Executable == ""
}
// IsJitDump returns whether the mapping looks like a jitdump[0] file.
//
// [0]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jitdump-specification.txt
func (pm *ExecutableMapping) IsJitDump() bool {
return strings.Contains(pm.Executable, "jit") && strings.HasSuffix(pm.Executable, ".dump")
}
// IsNotFileBacked returns whether the mapping is not backed by a
// file, such as JIT or vDSO sections.
func (pm *ExecutableMapping) IsNotFileBacked() bool {
return pm.IsJitted() || pm.IsSpecial()
}
// IsSpecial returns whether the file mapping is a "special" region,
// such as the mappings for vDSOs `[vdso]` and others.
func (pm *ExecutableMapping) IsSpecial() bool {
return len(pm.Executable) > 0 && pm.Executable[0] == '['
}
func (pm *ExecutableMapping) String() string {
return fmt.Sprintf("ExecutableMapping {LoadAddr: 0x%x, StartAddr: 0x%x, EndAddr: 0x%x, Executable:%s}", pm.LoadAddr, pm.StartAddr, pm.EndAddr, pm.Executable)
}
type ExecutableMappings []*ExecutableMapping
// HasJitted returns if there's at least one JIT'ed mapping.
func (pm ExecutableMappings) HasJitted() bool {
for _, execMapping := range pm {
if execMapping.IsJitted() {
return true
}
}
return false
}
// executableMappingCount returns the number of executable mappings
// in the passed `rawMappings`.
func executableMappingCount(rawMappings []*procfs.ProcMap) uint {
var executableMappingCount uint
for _, rawMapping := range rawMappings {
if rawMapping.Perms.Execute {
executableMappingCount += 1
}
}
return executableMappingCount
}
// ExecutableMappings returns the executable memory mappings with the appropriate
// loaded base address set for non-JIT code.
//
// The reason why we need to find the loaded base address is that ELF executables
// aren't typically loaded in one large executable section, but split in several
// mappings. For example, the .rodata section, as well as .eh_frame might go in
// sections without executable permissions, as they aren't needed.
func ListExecutableMappings(rawMappings []*procfs.ProcMap) ExecutableMappings {
result := make([]*ExecutableMapping, 0, executableMappingCount(rawMappings))
firstSeen := false
for idx, rawMapping := range rawMappings {
if rawMapping.Perms.Execute {
var loadAddr uint64
// We need the load base address for stack unwinding with DWARF
// information. We don't know of any runtimes that emit said unwind
// information for JITed code, so we set it to zero.
if rawMappings[idx].Pathname != "" {
for revIdx := idx; revIdx >= 0; revIdx-- {
if rawMappings[revIdx].Pathname != rawMappings[idx].Pathname {
break
}
loadAddr = uint64(rawMappings[revIdx].StartAddr)
}
}
result = append(result, &ExecutableMapping{
LoadAddr: loadAddr,
StartAddr: uint64(rawMapping.StartAddr),
EndAddr: uint64(rawMapping.EndAddr),
Executable: rawMapping.Pathname,
mainExec: !firstSeen,
})
firstSeen = true
}
}
return result
}