xref: /openbmc/linux/tools/perf/util/vdso.c (revision 609e478b)
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"
14 #include "machine.h"
15 #include "linux/string.h"
16 #include "debug.h"
17 
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 
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,
36 			.dso_name = DSO__NAME_VDSO,
37 		},
38 	};
39 
40 	return memdup(&vdso_info_init, sizeof(vdso_info_init));
41 }
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 
74 static char *get_file(struct vdso_file *vdso_file)
75 {
76 	char *vdso = NULL;
77 	char *buf = NULL;
78 	void *start, *end;
79 	size_t size;
80 	int fd;
81 
82 	if (vdso_file->found)
83 		return vdso_file->temp_file_name;
84 
85 	if (vdso_file->error || find_vdso_map(&start, &end))
86 		return NULL;
87 
88 	size = end - start;
89 
90 	buf = memdup(start, size);
91 	if (!buf)
92 		return NULL;
93 
94 	fd = mkstemp(vdso_file->temp_file_name);
95 	if (fd < 0)
96 		goto out;
97 
98 	if (size == (size_t) write(fd, buf, size))
99 		vdso = vdso_file->temp_file_name;
100 
101 	close(fd);
102 
103  out:
104 	free(buf);
105 
106 	vdso_file->found = (vdso != NULL);
107 	vdso_file->error = !vdso_file->found;
108 	return vdso;
109 }
110 
111 void vdso__exit(struct machine *machine)
112 {
113 	struct vdso_info *vdso_info = machine->vdso_info;
114 
115 	if (!vdso_info)
116 		return;
117 
118 	if (vdso_info->vdso.found)
119 		unlink(vdso_info->vdso.temp_file_name);
120 
121 	zfree(&machine->vdso_info);
122 }
123 
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 
138 struct dso *vdso__dso_findnew(struct machine *machine,
139 			      struct thread *thread __maybe_unused)
140 {
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;
150 
151 	dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true);
152 	if (!dso) {
153 		char *file;
154 
155 		file = get_file(&vdso_info->vdso);
156 		if (!file)
157 			return NULL;
158 
159 		dso = vdso__new(machine, DSO__NAME_VDSO, file);
160 	}
161 
162 	return dso;
163 }
164 
165 bool dso__is_vdso(struct dso *dso)
166 {
167 	return !strcmp(dso->short_name, DSO__NAME_VDSO);
168 }
169