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