xref: /openbmc/linux/tools/perf/util/vdso.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a43783aeSArnaldo Carvalho de Melo #include <errno.h>
37dbf4dcfSJiri Olsa #include <unistd.h>
47dbf4dcfSJiri Olsa #include <stdio.h>
57dbf4dcfSJiri Olsa #include <string.h>
67dbf4dcfSJiri Olsa #include <sys/types.h>
77dbf4dcfSJiri Olsa #include <sys/stat.h>
87dbf4dcfSJiri Olsa #include <fcntl.h>
97dbf4dcfSJiri Olsa #include <stdlib.h>
107dbf4dcfSJiri Olsa #include <linux/kernel.h>
117dbf4dcfSJiri Olsa 
127dbf4dcfSJiri Olsa #include "vdso.h"
134a3cec84SArnaldo Carvalho de Melo #include "dso.h"
14fb71c86cSArnaldo Carvalho de Melo #include <internal/lib.h>
151101f69aSArnaldo Carvalho de Melo #include "map.h"
167dbf4dcfSJiri Olsa #include "symbol.h"
172a03068cSAdrian Hunter #include "machine.h"
18f6832e17SAdrian Hunter #include "thread.h"
197dbf4dcfSJiri Olsa #include "linux/string.h"
207f7c536fSArnaldo Carvalho de Melo #include <linux/zalloc.h>
2184f5d36fSJiri Olsa #include "debug.h"
227dbf4dcfSJiri Olsa 
23e477f3f0SAdrian Hunter /*
2401153237SFlorian Fainelli  * Include definition of find_map() also used in perf-read-vdso.c for
25e477f3f0SAdrian Hunter  * building perf-read-vdso32 and perf-read-vdsox32.
26e477f3f0SAdrian Hunter  */
2701153237SFlorian Fainelli #include "find-map.c"
28e477f3f0SAdrian Hunter 
2930f4f815SAdrian Hunter #define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"
3030f4f815SAdrian Hunter 
3130f4f815SAdrian Hunter struct vdso_file {
3230f4f815SAdrian Hunter 	bool found;
3330f4f815SAdrian Hunter 	bool error;
3430f4f815SAdrian Hunter 	char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
3530f4f815SAdrian Hunter 	const char *dso_name;
36f6832e17SAdrian Hunter 	const char *read_prog;
3730f4f815SAdrian Hunter };
3830f4f815SAdrian Hunter 
3930f4f815SAdrian Hunter struct vdso_info {
4030f4f815SAdrian Hunter 	struct vdso_file vdso;
41f6832e17SAdrian Hunter #if BITS_PER_LONG == 64
42f6832e17SAdrian Hunter 	struct vdso_file vdso32;
43f6832e17SAdrian Hunter 	struct vdso_file vdsox32;
44f6832e17SAdrian Hunter #endif
4530f4f815SAdrian Hunter };
4630f4f815SAdrian Hunter 
vdso_info__new(void)47d027b640SAdrian Hunter static struct vdso_info *vdso_info__new(void)
48d027b640SAdrian Hunter {
49d027b640SAdrian Hunter 	static const struct vdso_info vdso_info_init = {
5030f4f815SAdrian Hunter 		.vdso    = {
5130f4f815SAdrian Hunter 			.temp_file_name = VDSO__TEMP_FILE_NAME,
5251682dc7SAdrian Hunter 			.dso_name = DSO__NAME_VDSO,
5330f4f815SAdrian Hunter 		},
54f6832e17SAdrian Hunter #if BITS_PER_LONG == 64
55f6832e17SAdrian Hunter 		.vdso32  = {
56f6832e17SAdrian Hunter 			.temp_file_name = VDSO__TEMP_FILE_NAME,
57f6832e17SAdrian Hunter 			.dso_name = DSO__NAME_VDSO32,
58f6832e17SAdrian Hunter 			.read_prog = "perf-read-vdso32",
59f6832e17SAdrian Hunter 		},
60f6832e17SAdrian Hunter 		.vdsox32  = {
61f6832e17SAdrian Hunter 			.temp_file_name = VDSO__TEMP_FILE_NAME,
62f6832e17SAdrian Hunter 			.dso_name = DSO__NAME_VDSOX32,
63f6832e17SAdrian Hunter 			.read_prog = "perf-read-vdsox32",
64f6832e17SAdrian Hunter 		},
65f6832e17SAdrian Hunter #endif
6630f4f815SAdrian Hunter 	};
6730f4f815SAdrian Hunter 
68d027b640SAdrian Hunter 	return memdup(&vdso_info_init, sizeof(vdso_info_init));
69d027b640SAdrian Hunter }
707dbf4dcfSJiri Olsa 
get_file(struct vdso_file * vdso_file)7130f4f815SAdrian Hunter static char *get_file(struct vdso_file *vdso_file)
727dbf4dcfSJiri Olsa {
737dbf4dcfSJiri Olsa 	char *vdso = NULL;
747dbf4dcfSJiri Olsa 	char *buf = NULL;
757dbf4dcfSJiri Olsa 	void *start, *end;
767dbf4dcfSJiri Olsa 	size_t size;
777dbf4dcfSJiri Olsa 	int fd;
787dbf4dcfSJiri Olsa 
7930f4f815SAdrian Hunter 	if (vdso_file->found)
8030f4f815SAdrian Hunter 		return vdso_file->temp_file_name;
817dbf4dcfSJiri Olsa 
8201153237SFlorian Fainelli 	if (vdso_file->error || find_map(&start, &end, VDSO__MAP_NAME))
837dbf4dcfSJiri Olsa 		return NULL;
847dbf4dcfSJiri Olsa 
857dbf4dcfSJiri Olsa 	size = end - start;
867dbf4dcfSJiri Olsa 
877dbf4dcfSJiri Olsa 	buf = memdup(start, size);
887dbf4dcfSJiri Olsa 	if (!buf)
897dbf4dcfSJiri Olsa 		return NULL;
907dbf4dcfSJiri Olsa 
9130f4f815SAdrian Hunter 	fd = mkstemp(vdso_file->temp_file_name);
927dbf4dcfSJiri Olsa 	if (fd < 0)
937dbf4dcfSJiri Olsa 		goto out;
947dbf4dcfSJiri Olsa 
957dbf4dcfSJiri Olsa 	if (size == (size_t) write(fd, buf, size))
9630f4f815SAdrian Hunter 		vdso = vdso_file->temp_file_name;
977dbf4dcfSJiri Olsa 
987dbf4dcfSJiri Olsa 	close(fd);
997dbf4dcfSJiri Olsa 
1007dbf4dcfSJiri Olsa  out:
1017dbf4dcfSJiri Olsa 	free(buf);
1027dbf4dcfSJiri Olsa 
10330f4f815SAdrian Hunter 	vdso_file->found = (vdso != NULL);
10430f4f815SAdrian Hunter 	vdso_file->error = !vdso_file->found;
1057dbf4dcfSJiri Olsa 	return vdso;
1067dbf4dcfSJiri Olsa }
1077dbf4dcfSJiri Olsa 
machine__exit_vdso(struct machine * machine)1089a4388c7SArnaldo Carvalho de Melo void machine__exit_vdso(struct machine *machine)
1097dbf4dcfSJiri Olsa {
110d027b640SAdrian Hunter 	struct vdso_info *vdso_info = machine->vdso_info;
111d027b640SAdrian Hunter 
112d027b640SAdrian Hunter 	if (!vdso_info)
113d027b640SAdrian Hunter 		return;
114d027b640SAdrian Hunter 
11530f4f815SAdrian Hunter 	if (vdso_info->vdso.found)
11630f4f815SAdrian Hunter 		unlink(vdso_info->vdso.temp_file_name);
117f6832e17SAdrian Hunter #if BITS_PER_LONG == 64
118f6832e17SAdrian Hunter 	if (vdso_info->vdso32.found)
119f6832e17SAdrian Hunter 		unlink(vdso_info->vdso32.temp_file_name);
120f6832e17SAdrian Hunter 	if (vdso_info->vdsox32.found)
121f6832e17SAdrian Hunter 		unlink(vdso_info->vdsox32.temp_file_name);
122f6832e17SAdrian Hunter #endif
123d027b640SAdrian Hunter 
124d027b640SAdrian Hunter 	zfree(&machine->vdso_info);
1257dbf4dcfSJiri Olsa }
1267dbf4dcfSJiri Olsa 
__machine__addnew_vdso(struct machine * machine,const char * short_name,const char * long_name)127e8807844SArnaldo Carvalho de Melo static struct dso *__machine__addnew_vdso(struct machine *machine, const char *short_name,
1284f71f2a0SAdrian Hunter 					  const char *long_name)
1294f71f2a0SAdrian Hunter {
1304f71f2a0SAdrian Hunter 	struct dso *dso;
1314f71f2a0SAdrian Hunter 
1324f71f2a0SAdrian Hunter 	dso = dso__new(short_name);
1334f71f2a0SAdrian Hunter 	if (dso != NULL) {
134e8807844SArnaldo Carvalho de Melo 		__dsos__add(&machine->dsos, dso);
1354f71f2a0SAdrian Hunter 		dso__set_long_name(dso, long_name, false);
13641d58541SNamhyung Kim 		/* Put dso here because __dsos_add already got it */
13741d58541SNamhyung Kim 		dso__put(dso);
1384f71f2a0SAdrian Hunter 	}
1394f71f2a0SAdrian Hunter 
1404f71f2a0SAdrian Hunter 	return dso;
1414f71f2a0SAdrian Hunter }
1424f71f2a0SAdrian Hunter 
machine__thread_dso_type(struct machine * machine,struct thread * thread)143f6832e17SAdrian Hunter static enum dso_type machine__thread_dso_type(struct machine *machine,
144f6832e17SAdrian Hunter 					      struct thread *thread)
145f6832e17SAdrian Hunter {
146f6832e17SAdrian Hunter 	enum dso_type dso_type = DSO__TYPE_UNKNOWN;
147ff583dc4SIan Rogers 	struct map_rb_node *rb_node;
148f6832e17SAdrian Hunter 
149ff583dc4SIan Rogers 	maps__for_each_entry(thread__maps(thread), rb_node) {
150*63df0e4bSIan Rogers 		struct dso *dso = map__dso(rb_node->map);
151ff583dc4SIan Rogers 
152f6832e17SAdrian Hunter 		if (!dso || dso->long_name[0] != '/')
153f6832e17SAdrian Hunter 			continue;
154f6832e17SAdrian Hunter 		dso_type = dso__type(dso, machine);
155f6832e17SAdrian Hunter 		if (dso_type != DSO__TYPE_UNKNOWN)
156f6832e17SAdrian Hunter 			break;
157f6832e17SAdrian Hunter 	}
158f6832e17SAdrian Hunter 
159f6832e17SAdrian Hunter 	return dso_type;
160f6832e17SAdrian Hunter }
161f6832e17SAdrian Hunter 
16276c588f1SHe Kuang #if BITS_PER_LONG == 64
16376c588f1SHe Kuang 
vdso__do_copy_compat(FILE * f,int fd)164f6832e17SAdrian Hunter static int vdso__do_copy_compat(FILE *f, int fd)
165f6832e17SAdrian Hunter {
166f6832e17SAdrian Hunter 	char buf[4096];
167f6832e17SAdrian Hunter 	size_t count;
168f6832e17SAdrian Hunter 
169f6832e17SAdrian Hunter 	while (1) {
170f6832e17SAdrian Hunter 		count = fread(buf, 1, sizeof(buf), f);
171f6832e17SAdrian Hunter 		if (ferror(f))
172f6832e17SAdrian Hunter 			return -errno;
173f6832e17SAdrian Hunter 		if (feof(f))
174f6832e17SAdrian Hunter 			break;
175f6832e17SAdrian Hunter 		if (count && writen(fd, buf, count) != (ssize_t)count)
176f6832e17SAdrian Hunter 			return -errno;
177f6832e17SAdrian Hunter 	}
178f6832e17SAdrian Hunter 
179f6832e17SAdrian Hunter 	return 0;
180f6832e17SAdrian Hunter }
181f6832e17SAdrian Hunter 
vdso__copy_compat(const char * prog,int fd)182f6832e17SAdrian Hunter static int vdso__copy_compat(const char *prog, int fd)
183f6832e17SAdrian Hunter {
184f6832e17SAdrian Hunter 	FILE *f;
185f6832e17SAdrian Hunter 	int err;
186f6832e17SAdrian Hunter 
187f6832e17SAdrian Hunter 	f = popen(prog, "r");
188f6832e17SAdrian Hunter 	if (!f)
189f6832e17SAdrian Hunter 		return -errno;
190f6832e17SAdrian Hunter 
191f6832e17SAdrian Hunter 	err = vdso__do_copy_compat(f, fd);
192f6832e17SAdrian Hunter 
193f6832e17SAdrian Hunter 	if (pclose(f) == -1)
194f6832e17SAdrian Hunter 		return -errno;
195f6832e17SAdrian Hunter 
196f6832e17SAdrian Hunter 	return err;
197f6832e17SAdrian Hunter }
198f6832e17SAdrian Hunter 
vdso__create_compat_file(const char * prog,char * temp_name)199f6832e17SAdrian Hunter static int vdso__create_compat_file(const char *prog, char *temp_name)
200f6832e17SAdrian Hunter {
201f6832e17SAdrian Hunter 	int fd, err;
202f6832e17SAdrian Hunter 
203f6832e17SAdrian Hunter 	fd = mkstemp(temp_name);
204f6832e17SAdrian Hunter 	if (fd < 0)
205f6832e17SAdrian Hunter 		return -errno;
206f6832e17SAdrian Hunter 
207f6832e17SAdrian Hunter 	err = vdso__copy_compat(prog, fd);
208f6832e17SAdrian Hunter 
209f6832e17SAdrian Hunter 	if (close(fd) == -1)
210f6832e17SAdrian Hunter 		return -errno;
211f6832e17SAdrian Hunter 
212f6832e17SAdrian Hunter 	return err;
213f6832e17SAdrian Hunter }
214f6832e17SAdrian Hunter 
vdso__get_compat_file(struct vdso_file * vdso_file)215f6832e17SAdrian Hunter static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
216f6832e17SAdrian Hunter {
217f6832e17SAdrian Hunter 	int err;
218f6832e17SAdrian Hunter 
219f6832e17SAdrian Hunter 	if (vdso_file->found)
220f6832e17SAdrian Hunter 		return vdso_file->temp_file_name;
221f6832e17SAdrian Hunter 
222f6832e17SAdrian Hunter 	if (vdso_file->error)
223f6832e17SAdrian Hunter 		return NULL;
224f6832e17SAdrian Hunter 
225f6832e17SAdrian Hunter 	err = vdso__create_compat_file(vdso_file->read_prog,
226f6832e17SAdrian Hunter 				       vdso_file->temp_file_name);
227f6832e17SAdrian Hunter 	if (err) {
228f6832e17SAdrian Hunter 		pr_err("%s failed, error %d\n", vdso_file->read_prog, err);
229f6832e17SAdrian Hunter 		vdso_file->error = true;
230f6832e17SAdrian Hunter 		return NULL;
231f6832e17SAdrian Hunter 	}
232f6832e17SAdrian Hunter 
233f6832e17SAdrian Hunter 	vdso_file->found = true;
234f6832e17SAdrian Hunter 
235f6832e17SAdrian Hunter 	return vdso_file->temp_file_name;
236f6832e17SAdrian Hunter }
237f6832e17SAdrian Hunter 
__machine__findnew_compat(struct machine * machine,struct vdso_file * vdso_file)238e8807844SArnaldo Carvalho de Melo static struct dso *__machine__findnew_compat(struct machine *machine,
239f6832e17SAdrian Hunter 					     struct vdso_file *vdso_file)
240f6832e17SAdrian Hunter {
241f6832e17SAdrian Hunter 	const char *file_name;
242f6832e17SAdrian Hunter 	struct dso *dso;
243f6832e17SAdrian Hunter 
244e8807844SArnaldo Carvalho de Melo 	dso = __dsos__find(&machine->dsos, vdso_file->dso_name, true);
245f6832e17SAdrian Hunter 	if (dso)
2466d545a63SAdrian Hunter 		goto out;
247f6832e17SAdrian Hunter 
248f6832e17SAdrian Hunter 	file_name = vdso__get_compat_file(vdso_file);
249f6832e17SAdrian Hunter 	if (!file_name)
2506d545a63SAdrian Hunter 		goto out;
251f6832e17SAdrian Hunter 
252e8807844SArnaldo Carvalho de Melo 	dso = __machine__addnew_vdso(machine, vdso_file->dso_name, file_name);
2536d545a63SAdrian Hunter out:
254e8807844SArnaldo Carvalho de Melo 	return dso;
255f6832e17SAdrian Hunter }
256f6832e17SAdrian Hunter 
__machine__findnew_vdso_compat(struct machine * machine,struct thread * thread,struct vdso_info * vdso_info,struct dso ** dso)257e8807844SArnaldo Carvalho de Melo static int __machine__findnew_vdso_compat(struct machine *machine,
258f6832e17SAdrian Hunter 					  struct thread *thread,
259f6832e17SAdrian Hunter 					  struct vdso_info *vdso_info,
260f6832e17SAdrian Hunter 					  struct dso **dso)
261f6832e17SAdrian Hunter {
262f6832e17SAdrian Hunter 	enum dso_type dso_type;
263f6832e17SAdrian Hunter 
264f6832e17SAdrian Hunter 	dso_type = machine__thread_dso_type(machine, thread);
26546b1fa85SAdrian Hunter 
26646b1fa85SAdrian Hunter #ifndef HAVE_PERF_READ_VDSO32
26746b1fa85SAdrian Hunter 	if (dso_type == DSO__TYPE_32BIT)
26846b1fa85SAdrian Hunter 		return 0;
26946b1fa85SAdrian Hunter #endif
27046b1fa85SAdrian Hunter #ifndef HAVE_PERF_READ_VDSOX32
27146b1fa85SAdrian Hunter 	if (dso_type == DSO__TYPE_X32BIT)
27246b1fa85SAdrian Hunter 		return 0;
27346b1fa85SAdrian Hunter #endif
27446b1fa85SAdrian Hunter 
275f6832e17SAdrian Hunter 	switch (dso_type) {
276f6832e17SAdrian Hunter 	case DSO__TYPE_32BIT:
277e8807844SArnaldo Carvalho de Melo 		*dso = __machine__findnew_compat(machine, &vdso_info->vdso32);
278f6832e17SAdrian Hunter 		return 1;
279f6832e17SAdrian Hunter 	case DSO__TYPE_X32BIT:
280e8807844SArnaldo Carvalho de Melo 		*dso = __machine__findnew_compat(machine, &vdso_info->vdsox32);
281f6832e17SAdrian Hunter 		return 1;
282f6832e17SAdrian Hunter 	case DSO__TYPE_UNKNOWN:
283f6832e17SAdrian Hunter 	case DSO__TYPE_64BIT:
284f6832e17SAdrian Hunter 	default:
285f6832e17SAdrian Hunter 		return 0;
286f6832e17SAdrian Hunter 	}
287f6832e17SAdrian Hunter }
288f6832e17SAdrian Hunter 
289f6832e17SAdrian Hunter #endif
290f6832e17SAdrian Hunter 
machine__find_vdso(struct machine * machine,struct thread * thread)29176c588f1SHe Kuang static struct dso *machine__find_vdso(struct machine *machine,
29276c588f1SHe Kuang 				      struct thread *thread)
29376c588f1SHe Kuang {
29476c588f1SHe Kuang 	struct dso *dso = NULL;
29576c588f1SHe Kuang 	enum dso_type dso_type;
29676c588f1SHe Kuang 
29776c588f1SHe Kuang 	dso_type = machine__thread_dso_type(machine, thread);
29876c588f1SHe Kuang 	switch (dso_type) {
29976c588f1SHe Kuang 	case DSO__TYPE_32BIT:
30076c588f1SHe Kuang 		dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO32, true);
30176c588f1SHe Kuang 		if (!dso) {
30276c588f1SHe Kuang 			dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO,
30376c588f1SHe Kuang 					   true);
30476c588f1SHe Kuang 			if (dso && dso_type != dso__type(dso, machine))
30576c588f1SHe Kuang 				dso = NULL;
30676c588f1SHe Kuang 		}
30776c588f1SHe Kuang 		break;
30876c588f1SHe Kuang 	case DSO__TYPE_X32BIT:
30976c588f1SHe Kuang 		dso = __dsos__find(&machine->dsos, DSO__NAME_VDSOX32, true);
31076c588f1SHe Kuang 		break;
31176c588f1SHe Kuang 	case DSO__TYPE_64BIT:
31276c588f1SHe Kuang 	case DSO__TYPE_UNKNOWN:
31376c588f1SHe Kuang 	default:
31476c588f1SHe Kuang 		dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
31576c588f1SHe Kuang 		break;
31676c588f1SHe Kuang 	}
31776c588f1SHe Kuang 
31876c588f1SHe Kuang 	return dso;
31976c588f1SHe Kuang }
32076c588f1SHe Kuang 
machine__findnew_vdso(struct machine * machine,struct thread * thread)3219a4388c7SArnaldo Carvalho de Melo struct dso *machine__findnew_vdso(struct machine *machine,
32276c588f1SHe Kuang 				  struct thread *thread)
3237dbf4dcfSJiri Olsa {
324d027b640SAdrian Hunter 	struct vdso_info *vdso_info;
325e8807844SArnaldo Carvalho de Melo 	struct dso *dso = NULL;
3267dbf4dcfSJiri Olsa 
3270a7c74eaSArnaldo Carvalho de Melo 	down_write(&machine->dsos.lock);
328d027b640SAdrian Hunter 	if (!machine->vdso_info)
329d027b640SAdrian Hunter 		machine->vdso_info = vdso_info__new();
330d027b640SAdrian Hunter 
331d027b640SAdrian Hunter 	vdso_info = machine->vdso_info;
332d027b640SAdrian Hunter 	if (!vdso_info)
333e8807844SArnaldo Carvalho de Melo 		goto out_unlock;
334d027b640SAdrian Hunter 
33576c588f1SHe Kuang 	dso = machine__find_vdso(machine, thread);
33676c588f1SHe Kuang 	if (dso)
33776c588f1SHe Kuang 		goto out_unlock;
33876c588f1SHe Kuang 
339f6832e17SAdrian Hunter #if BITS_PER_LONG == 64
340e8807844SArnaldo Carvalho de Melo 	if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso))
341e8807844SArnaldo Carvalho de Melo 		goto out_unlock;
342f6832e17SAdrian Hunter #endif
343f6832e17SAdrian Hunter 
344e8807844SArnaldo Carvalho de Melo 	dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true);
3457dbf4dcfSJiri Olsa 	if (!dso) {
3467dbf4dcfSJiri Olsa 		char *file;
3477dbf4dcfSJiri Olsa 
34830f4f815SAdrian Hunter 		file = get_file(&vdso_info->vdso);
349e8807844SArnaldo Carvalho de Melo 		if (file)
350e8807844SArnaldo Carvalho de Melo 			dso = __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);
3517dbf4dcfSJiri Olsa 	}
3527dbf4dcfSJiri Olsa 
353e8807844SArnaldo Carvalho de Melo out_unlock:
354d3a7c489SArnaldo Carvalho de Melo 	dso__get(dso);
3550a7c74eaSArnaldo Carvalho de Melo 	up_write(&machine->dsos.lock);
3567dbf4dcfSJiri Olsa 	return dso;
3577dbf4dcfSJiri Olsa }
35851682dc7SAdrian Hunter 
dso__is_vdso(struct dso * dso)35951682dc7SAdrian Hunter bool dso__is_vdso(struct dso *dso)
36051682dc7SAdrian Hunter {
361f6832e17SAdrian Hunter 	return !strcmp(dso->short_name, DSO__NAME_VDSO) ||
362f6832e17SAdrian Hunter 	       !strcmp(dso->short_name, DSO__NAME_VDSO32) ||
363f6832e17SAdrian Hunter 	       !strcmp(dso->short_name, DSO__NAME_VDSOX32);
36451682dc7SAdrian Hunter }
365