-
Notifications
You must be signed in to change notification settings - Fork 10
/
syscalls.go
130 lines (110 loc) · 3.28 KB
/
syscalls.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
package manager
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"runtime"
"strings"
)
// cache of the syscall prefix depending on kernel version
var syscallPrefix string
// GetSyscallFnName - Returns the kernel function of the provided syscall, after reading /proc/kallsyms to retrieve
// the list of symbols of the current kernel.
func GetSyscallFnName(name string) (string, error) {
return GetSyscallFnNameWithSymFile(name, defaultSymFile)
}
// GetSyscallFnNameWithSymFile - Returns the kernel function of the provided syscall, after reading symFile to retrieve
// the list of symbols of the current kernel.
func GetSyscallFnNameWithSymFile(name string, symFile string) (string, error) {
if symFile == "" {
symFile = defaultSymFile
}
if syscallPrefix == "" {
syscallName, err := getSyscallName("open", symFile)
if err != nil {
return "", err
}
// copy to avoid memory leak due to go subslice
// see: https://go101.org/article/memory-leaking.html
var b strings.Builder
b.WriteString(syscallName)
syscallName = b.String()
syscallPrefix = strings.TrimSuffix(syscallName, "open")
}
return syscallPrefix + name, nil
}
const defaultSymFile = "/proc/kallsyms"
// Returns the qualified syscall named by going through '/proc/kallsyms' on the
// system on which its executed. It allows bpf programs that may have been compiled
// for older syscall functions to run on newer kernels
func getSyscallName(name string, symFile string) (string, error) {
// Get kernel symbols
syms, err := os.Open(symFile)
if err != nil {
return "", err
}
defer syms.Close()
return getSyscallFnNameWithKallsyms(name, syms, "")
}
func getSyscallFnNameWithKallsyms(name string, kallsymsContent io.Reader, arch string) (string, error) {
if arch == "" {
switch runtime.GOARCH {
case "386":
arch = "ia32"
case "arm64":
arch = "arm64"
default:
arch = "x64"
}
}
// We should search for new syscall function like "__x64__sys_open"
// Note the start of word boundary. Should return exactly one string
newSyscall := regexp.MustCompile(`\b__` + arch + `_[Ss]y[sS]_` + name + `\b`)
// If nothing found, search for old syscall function to be sure
oldSyscall := regexp.MustCompile(`\b[Ss]y[sS]_` + name + `\b`)
// check for '__' prefixed functions, like '__sys_open'
prefixed := regexp.MustCompile(`\b__[Ss]y[sS]_` + name + `\b`)
// the order of patterns is important
// we first want to look for the new syscall format, then the old format, then the prefixed format
patterns := []struct {
pattern *regexp.Regexp
result string
}{
{newSyscall, ""},
{oldSyscall, ""},
{prefixed, ""},
}
scanner := bufio.NewScanner(kallsymsContent)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
line := scanner.Text()
if !strings.Contains(line, name) {
continue
}
for i := range patterns {
p := &patterns[i]
// if we already found a match for this pattern we continue
if p.result != "" {
continue
}
if res := p.pattern.FindString(line); res != "" {
// fast path for first match on first pattern
if i == 0 {
return res, nil
}
p.result = res
}
}
}
if err := scanner.Err(); err != nil {
return "", err
}
for _, p := range patterns {
if p.result != "" {
return p.result, nil
}
}
return "", fmt.Errorf("could not find a valid syscall name")
}