forked from oracle/smith
-
Notifications
You must be signed in to change notification settings - Fork 0
/
deps.go
154 lines (142 loc) · 4.32 KB
/
deps.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package main
import (
"debug/elf"
"os"
"path/filepath"
"strings"
"github.com/Sirupsen/logrus"
)
var (
soMap map[string]string
preloadPaths []string
)
type executor func(name string, arg ...string) (string, string, error)
// SetSoPathsFromExecutor executes ldconfig using the given executor
// the executor should be a function which takes string args and returns
// stdout, stderr, error. The executor is responsible for setting up the
// proper environment for ldconfig, by chrooting for example.
func SetSoPathsFromExecutor(ex executor, preload []string) error {
stdout, stderr, err := ex("ldconfig", "-v", "-N", "-X", "/")
if err != nil {
logrus.Warnf("ldconfig failed: %v", strings.TrimSpace(stderr))
return nil
}
SetSoPaths(stdout, preload)
return nil
}
// SetSoPaths parses a string formatted like the output from
// ldconfig -v -N -X and stores the so paths for later use by Deps
func SetSoPaths(ldconfigout string, preload []string) {
lines := strings.Split(ldconfigout, "\n")
soMap = map[string]string{}
preloadPaths = preload
path := ""
for _, line := range lines {
if len(line) == 0 {
continue
}
loc := strings.IndexRune(line, ':')
if line[0] == '/' && loc != -1 {
path = line[:loc]
if strings.HasSuffix(path, ".so") {
path = filepath.Dir(path)
}
continue
}
// lines here epresent the mapping of the generic so name to a
// specific version <source> -> <target>
parts := strings.Split(line, "->")
if len(parts) > 1 {
// we use the source name for the mapping because we want to
// keep the symlink around for the loader
source := strings.TrimSpace(parts[0])
if _, ok := soMap[source]; !ok {
soMap[source] = filepath.Join(path, source)
}
}
}
}
func FindLibrary(library, chrootDir string, paths []string) string {
for _, path := range paths {
full := filepath.Clean(filepath.Join(path, library))
if _, err := os.Lstat(filepath.Join(chrootDir, full)); err == nil {
return full
}
}
if soMap != nil {
full := soMap[library]
if full != "" {
return full
}
}
// if ldconfig didn't give us any useful files to lookup (like on alpine), we
// may need to manually search some paths, so lets add the common ones
for _, path := range []string{"/lib", "/usr/lib", "/usr/local/lib"} {
full := filepath.Clean(filepath.Join(path, library))
logrus.Debugf("Checking for %v", full)
if _, err := os.Lstat(filepath.Join(chrootDir, full)); err == nil {
return full
}
}
return ""
}
// Deps recursively finds all statically linked dependencies of the executable
// in path within the given chroot. If nss is true, it also includes the
// relevant libnss libraries.
func Deps(chrootDir, path string, nss bool) (map[string]struct{}, error) {
var result = map[string]struct{}{}
elfFile, err := elf.Open(path)
if err != nil {
// not an elf, return empty
logrus.Debugf("%v is not an ELF", path)
return result, nil
}
defer elfFile.Close()
needs, err := elfFile.DynString(elf.DT_NEEDED)
if err != nil || needs == nil {
return result, nil
}
paths := preloadPaths
runpaths, err := elfFile.DynString(elf.DT_RUNPATH)
shortPath := strings.TrimPrefix(path, chrootDir)
origin := filepath.Dir(shortPath)
if err == nil && runpaths != nil {
fixed := strings.Replace(runpaths[0], "$ORIGIN", origin, -1)
paths = append(paths, strings.Split(fixed, ":")...)
} else {
rpaths, err := elfFile.DynString(elf.DT_RPATH)
if err == nil && rpaths != nil {
fixed := strings.Replace(rpaths[0], "$ORIGIN", origin, -1)
paths = append(paths, strings.Split(fixed, ":")...)
}
}
if nss {
for _, s := range []string{"libnss_dns.so.2", "libnss_files.so.2", "libnss_compat.so.2"} {
full := FindLibrary(s, chrootDir, paths)
if full != "" {
logrus.Debugf("%v adding nss library: %v", shortPath, full)
result[full] = struct{}{}
}
}
nss = false
}
for _, need := range needs {
full := FindLibrary(need, chrootDir, paths)
if full != "" {
logrus.Debugf("%v depends on library: %v", shortPath, full)
result[full] = struct{}{}
continue
}
logrus.Warnf("Unable to locate %s for %s", need, shortPath)
}
section := elfFile.Section(".interp")
if section != nil {
data, err := section.Data()
if err == nil {
interp := string(data[:len(data)-1])
logrus.Debugf("%v uses interp: %v", shortPath, interp)
result[interp] = struct{}{}
}
}
return result, nil
}