Commit | Line | Data |
---|---|---|
7dbf4dcf JO |
1 | |
2 | #include <unistd.h> | |
3 | #include <stdio.h> | |
4 | #include <string.h> | |
5 | #include <sys/types.h> | |
6 | #include <sys/stat.h> | |
7 | #include <fcntl.h> | |
8 | #include <stdlib.h> | |
9 | #include <linux/kernel.h> | |
10 | ||
11 | #include "vdso.h" | |
12 | #include "util.h" | |
13 | #include "symbol.h" | |
2a03068c | 14 | #include "machine.h" |
7dbf4dcf | 15 | #include "linux/string.h" |
84f5d36f | 16 | #include "debug.h" |
7dbf4dcf | 17 | |
30f4f815 AH |
18 | #define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX" |
19 | ||
20 | struct vdso_file { | |
21 | bool found; | |
22 | bool error; | |
23 | char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)]; | |
24 | const char *dso_name; | |
25 | }; | |
26 | ||
27 | struct vdso_info { | |
28 | struct vdso_file vdso; | |
29 | }; | |
30 | ||
d027b640 AH |
31 | static struct vdso_info *vdso_info__new(void) |
32 | { | |
33 | static const struct vdso_info vdso_info_init = { | |
34 | .vdso = { | |
35 | .temp_file_name = VDSO__TEMP_FILE_NAME, | |
51682dc7 | 36 | .dso_name = DSO__NAME_VDSO, |
d027b640 AH |
37 | }, |
38 | }; | |
39 | ||
40 | return memdup(&vdso_info_init, sizeof(vdso_info_init)); | |
41 | } | |
7dbf4dcf JO |
42 | |
43 | static int find_vdso_map(void **start, void **end) | |
44 | { | |
45 | FILE *maps; | |
46 | char line[128]; | |
47 | int found = 0; | |
48 | ||
49 | maps = fopen("/proc/self/maps", "r"); | |
50 | if (!maps) { | |
51 | pr_err("vdso: cannot open maps\n"); | |
52 | return -1; | |
53 | } | |
54 | ||
55 | while (!found && fgets(line, sizeof(line), maps)) { | |
56 | int m = -1; | |
57 | ||
58 | /* We care only about private r-x mappings. */ | |
59 | if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n", | |
60 | start, end, &m)) | |
61 | continue; | |
62 | if (m < 0) | |
63 | continue; | |
64 | ||
65 | if (!strncmp(&line[m], VDSO__MAP_NAME, | |
66 | sizeof(VDSO__MAP_NAME) - 1)) | |
67 | found = 1; | |
68 | } | |
69 | ||
70 | fclose(maps); | |
71 | return !found; | |
72 | } | |
73 | ||
30f4f815 | 74 | static char *get_file(struct vdso_file *vdso_file) |
7dbf4dcf JO |
75 | { |
76 | char *vdso = NULL; | |
77 | char *buf = NULL; | |
78 | void *start, *end; | |
79 | size_t size; | |
80 | int fd; | |
81 | ||
30f4f815 AH |
82 | if (vdso_file->found) |
83 | return vdso_file->temp_file_name; | |
7dbf4dcf | 84 | |
30f4f815 | 85 | if (vdso_file->error || find_vdso_map(&start, &end)) |
7dbf4dcf JO |
86 | return NULL; |
87 | ||
88 | size = end - start; | |
89 | ||
90 | buf = memdup(start, size); | |
91 | if (!buf) | |
92 | return NULL; | |
93 | ||
30f4f815 | 94 | fd = mkstemp(vdso_file->temp_file_name); |
7dbf4dcf JO |
95 | if (fd < 0) |
96 | goto out; | |
97 | ||
98 | if (size == (size_t) write(fd, buf, size)) | |
30f4f815 | 99 | vdso = vdso_file->temp_file_name; |
7dbf4dcf JO |
100 | |
101 | close(fd); | |
102 | ||
103 | out: | |
104 | free(buf); | |
105 | ||
30f4f815 AH |
106 | vdso_file->found = (vdso != NULL); |
107 | vdso_file->error = !vdso_file->found; | |
7dbf4dcf JO |
108 | return vdso; |
109 | } | |
110 | ||
d027b640 | 111 | void vdso__exit(struct machine *machine) |
7dbf4dcf | 112 | { |
d027b640 AH |
113 | struct vdso_info *vdso_info = machine->vdso_info; |
114 | ||
115 | if (!vdso_info) | |
116 | return; | |
117 | ||
30f4f815 AH |
118 | if (vdso_info->vdso.found) |
119 | unlink(vdso_info->vdso.temp_file_name); | |
d027b640 AH |
120 | |
121 | zfree(&machine->vdso_info); | |
7dbf4dcf JO |
122 | } |
123 | ||
4f71f2a0 AH |
124 | static struct dso *vdso__new(struct machine *machine, const char *short_name, |
125 | const char *long_name) | |
126 | { | |
127 | struct dso *dso; | |
128 | ||
129 | dso = dso__new(short_name); | |
130 | if (dso != NULL) { | |
131 | dsos__add(&machine->user_dsos, dso); | |
132 | dso__set_long_name(dso, long_name, false); | |
133 | } | |
134 | ||
135 | return dso; | |
136 | } | |
137 | ||
5835edda AH |
138 | struct dso *vdso__dso_findnew(struct machine *machine, |
139 | struct thread *thread __maybe_unused) | |
7dbf4dcf | 140 | { |
d027b640 AH |
141 | struct vdso_info *vdso_info; |
142 | struct dso *dso; | |
143 | ||
144 | if (!machine->vdso_info) | |
145 | machine->vdso_info = vdso_info__new(); | |
146 | ||
147 | vdso_info = machine->vdso_info; | |
148 | if (!vdso_info) | |
149 | return NULL; | |
7dbf4dcf | 150 | |
51682dc7 | 151 | dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true); |
7dbf4dcf JO |
152 | if (!dso) { |
153 | char *file; | |
154 | ||
30f4f815 | 155 | file = get_file(&vdso_info->vdso); |
7dbf4dcf JO |
156 | if (!file) |
157 | return NULL; | |
158 | ||
51682dc7 | 159 | dso = vdso__new(machine, DSO__NAME_VDSO, file); |
7dbf4dcf JO |
160 | } |
161 | ||
162 | return dso; | |
163 | } | |
51682dc7 AH |
164 | |
165 | bool dso__is_vdso(struct dso *dso) | |
166 | { | |
167 | return !strcmp(dso->short_name, DSO__NAME_VDSO); | |
168 | } |