18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <errno.h> 38c2ecf20Sopenharmony_ci#include <unistd.h> 48c2ecf20Sopenharmony_ci#include <stdio.h> 58c2ecf20Sopenharmony_ci#include <string.h> 68c2ecf20Sopenharmony_ci#include <sys/types.h> 78c2ecf20Sopenharmony_ci#include <sys/stat.h> 88c2ecf20Sopenharmony_ci#include <fcntl.h> 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "vdso.h" 138c2ecf20Sopenharmony_ci#include "dso.h" 148c2ecf20Sopenharmony_ci#include <internal/lib.h> 158c2ecf20Sopenharmony_ci#include "map.h" 168c2ecf20Sopenharmony_ci#include "symbol.h" 178c2ecf20Sopenharmony_ci#include "machine.h" 188c2ecf20Sopenharmony_ci#include "thread.h" 198c2ecf20Sopenharmony_ci#include "linux/string.h" 208c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 218c2ecf20Sopenharmony_ci#include "debug.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * Include definition of find_map() also used in perf-read-vdso.c for 258c2ecf20Sopenharmony_ci * building perf-read-vdso32 and perf-read-vdsox32. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci#include "find-map.c" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct vdso_file { 328c2ecf20Sopenharmony_ci bool found; 338c2ecf20Sopenharmony_ci bool error; 348c2ecf20Sopenharmony_ci char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)]; 358c2ecf20Sopenharmony_ci const char *dso_name; 368c2ecf20Sopenharmony_ci const char *read_prog; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct vdso_info { 408c2ecf20Sopenharmony_ci struct vdso_file vdso; 418c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 64 428c2ecf20Sopenharmony_ci struct vdso_file vdso32; 438c2ecf20Sopenharmony_ci struct vdso_file vdsox32; 448c2ecf20Sopenharmony_ci#endif 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct vdso_info *vdso_info__new(void) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci static const struct vdso_info vdso_info_init = { 508c2ecf20Sopenharmony_ci .vdso = { 518c2ecf20Sopenharmony_ci .temp_file_name = VDSO__TEMP_FILE_NAME, 528c2ecf20Sopenharmony_ci .dso_name = DSO__NAME_VDSO, 538c2ecf20Sopenharmony_ci }, 548c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 64 558c2ecf20Sopenharmony_ci .vdso32 = { 568c2ecf20Sopenharmony_ci .temp_file_name = VDSO__TEMP_FILE_NAME, 578c2ecf20Sopenharmony_ci .dso_name = DSO__NAME_VDSO32, 588c2ecf20Sopenharmony_ci .read_prog = "perf-read-vdso32", 598c2ecf20Sopenharmony_ci }, 608c2ecf20Sopenharmony_ci .vdsox32 = { 618c2ecf20Sopenharmony_ci .temp_file_name = VDSO__TEMP_FILE_NAME, 628c2ecf20Sopenharmony_ci .dso_name = DSO__NAME_VDSOX32, 638c2ecf20Sopenharmony_ci .read_prog = "perf-read-vdsox32", 648c2ecf20Sopenharmony_ci }, 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci }; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return memdup(&vdso_info_init, sizeof(vdso_info_init)); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic char *get_file(struct vdso_file *vdso_file) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci char *vdso = NULL; 748c2ecf20Sopenharmony_ci char *buf = NULL; 758c2ecf20Sopenharmony_ci void *start, *end; 768c2ecf20Sopenharmony_ci size_t size; 778c2ecf20Sopenharmony_ci int fd; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (vdso_file->found) 808c2ecf20Sopenharmony_ci return vdso_file->temp_file_name; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (vdso_file->error || find_map(&start, &end, VDSO__MAP_NAME)) 838c2ecf20Sopenharmony_ci return NULL; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci size = end - start; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci buf = memdup(start, size); 888c2ecf20Sopenharmony_ci if (!buf) 898c2ecf20Sopenharmony_ci return NULL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci fd = mkstemp(vdso_file->temp_file_name); 928c2ecf20Sopenharmony_ci if (fd < 0) 938c2ecf20Sopenharmony_ci goto out; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (size == (size_t) write(fd, buf, size)) 968c2ecf20Sopenharmony_ci vdso = vdso_file->temp_file_name; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci close(fd); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci out: 1018c2ecf20Sopenharmony_ci free(buf); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci vdso_file->found = (vdso != NULL); 1048c2ecf20Sopenharmony_ci vdso_file->error = !vdso_file->found; 1058c2ecf20Sopenharmony_ci return vdso; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_civoid machine__exit_vdso(struct machine *machine) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct vdso_info *vdso_info = machine->vdso_info; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!vdso_info) 1138c2ecf20Sopenharmony_ci return; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (vdso_info->vdso.found) 1168c2ecf20Sopenharmony_ci unlink(vdso_info->vdso.temp_file_name); 1178c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 64 1188c2ecf20Sopenharmony_ci if (vdso_info->vdso32.found) 1198c2ecf20Sopenharmony_ci unlink(vdso_info->vdso32.temp_file_name); 1208c2ecf20Sopenharmony_ci if (vdso_info->vdsox32.found) 1218c2ecf20Sopenharmony_ci unlink(vdso_info->vdsox32.temp_file_name); 1228c2ecf20Sopenharmony_ci#endif 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci zfree(&machine->vdso_info); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic struct dso *__machine__addnew_vdso(struct machine *machine, const char *short_name, 1288c2ecf20Sopenharmony_ci const char *long_name) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct dso *dso; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci dso = dso__new(short_name); 1338c2ecf20Sopenharmony_ci if (dso != NULL) { 1348c2ecf20Sopenharmony_ci __dsos__add(&machine->dsos, dso); 1358c2ecf20Sopenharmony_ci dso__set_long_name(dso, long_name, false); 1368c2ecf20Sopenharmony_ci /* Put dso here because __dsos_add already got it */ 1378c2ecf20Sopenharmony_ci dso__put(dso); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return dso; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic enum dso_type machine__thread_dso_type(struct machine *machine, 1448c2ecf20Sopenharmony_ci struct thread *thread) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci enum dso_type dso_type = DSO__TYPE_UNKNOWN; 1478c2ecf20Sopenharmony_ci struct map *map; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci maps__for_each_entry(thread->maps, map) { 1508c2ecf20Sopenharmony_ci struct dso *dso = map->dso; 1518c2ecf20Sopenharmony_ci if (!dso || dso->long_name[0] != '/') 1528c2ecf20Sopenharmony_ci continue; 1538c2ecf20Sopenharmony_ci dso_type = dso__type(dso, machine); 1548c2ecf20Sopenharmony_ci if (dso_type != DSO__TYPE_UNKNOWN) 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return dso_type; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 64 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int vdso__do_copy_compat(FILE *f, int fd) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci char buf[4096]; 1668c2ecf20Sopenharmony_ci size_t count; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci while (1) { 1698c2ecf20Sopenharmony_ci count = fread(buf, 1, sizeof(buf), f); 1708c2ecf20Sopenharmony_ci if (ferror(f)) 1718c2ecf20Sopenharmony_ci return -errno; 1728c2ecf20Sopenharmony_ci if (feof(f)) 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci if (count && writen(fd, buf, count) != (ssize_t)count) 1758c2ecf20Sopenharmony_ci return -errno; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int vdso__copy_compat(const char *prog, int fd) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci FILE *f; 1848c2ecf20Sopenharmony_ci int err; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci f = popen(prog, "r"); 1878c2ecf20Sopenharmony_ci if (!f) 1888c2ecf20Sopenharmony_ci return -errno; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci err = vdso__do_copy_compat(f, fd); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (pclose(f) == -1) 1938c2ecf20Sopenharmony_ci return -errno; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return err; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int vdso__create_compat_file(const char *prog, char *temp_name) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci int fd, err; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci fd = mkstemp(temp_name); 2038c2ecf20Sopenharmony_ci if (fd < 0) 2048c2ecf20Sopenharmony_ci return -errno; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci err = vdso__copy_compat(prog, fd); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (close(fd) == -1) 2098c2ecf20Sopenharmony_ci return -errno; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return err; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic const char *vdso__get_compat_file(struct vdso_file *vdso_file) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci int err; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (vdso_file->found) 2198c2ecf20Sopenharmony_ci return vdso_file->temp_file_name; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (vdso_file->error) 2228c2ecf20Sopenharmony_ci return NULL; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci err = vdso__create_compat_file(vdso_file->read_prog, 2258c2ecf20Sopenharmony_ci vdso_file->temp_file_name); 2268c2ecf20Sopenharmony_ci if (err) { 2278c2ecf20Sopenharmony_ci pr_err("%s failed, error %d\n", vdso_file->read_prog, err); 2288c2ecf20Sopenharmony_ci vdso_file->error = true; 2298c2ecf20Sopenharmony_ci return NULL; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci vdso_file->found = true; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return vdso_file->temp_file_name; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic struct dso *__machine__findnew_compat(struct machine *machine, 2388c2ecf20Sopenharmony_ci struct vdso_file *vdso_file) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci const char *file_name; 2418c2ecf20Sopenharmony_ci struct dso *dso; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci dso = __dsos__find(&machine->dsos, vdso_file->dso_name, true); 2448c2ecf20Sopenharmony_ci if (dso) 2458c2ecf20Sopenharmony_ci goto out; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci file_name = vdso__get_compat_file(vdso_file); 2488c2ecf20Sopenharmony_ci if (!file_name) 2498c2ecf20Sopenharmony_ci goto out; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dso = __machine__addnew_vdso(machine, vdso_file->dso_name, file_name); 2528c2ecf20Sopenharmony_ciout: 2538c2ecf20Sopenharmony_ci return dso; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int __machine__findnew_vdso_compat(struct machine *machine, 2578c2ecf20Sopenharmony_ci struct thread *thread, 2588c2ecf20Sopenharmony_ci struct vdso_info *vdso_info, 2598c2ecf20Sopenharmony_ci struct dso **dso) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci enum dso_type dso_type; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci dso_type = machine__thread_dso_type(machine, thread); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#ifndef HAVE_PERF_READ_VDSO32 2668c2ecf20Sopenharmony_ci if (dso_type == DSO__TYPE_32BIT) 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci#endif 2698c2ecf20Sopenharmony_ci#ifndef HAVE_PERF_READ_VDSOX32 2708c2ecf20Sopenharmony_ci if (dso_type == DSO__TYPE_X32BIT) 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci#endif 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci switch (dso_type) { 2758c2ecf20Sopenharmony_ci case DSO__TYPE_32BIT: 2768c2ecf20Sopenharmony_ci *dso = __machine__findnew_compat(machine, &vdso_info->vdso32); 2778c2ecf20Sopenharmony_ci return 1; 2788c2ecf20Sopenharmony_ci case DSO__TYPE_X32BIT: 2798c2ecf20Sopenharmony_ci *dso = __machine__findnew_compat(machine, &vdso_info->vdsox32); 2808c2ecf20Sopenharmony_ci return 1; 2818c2ecf20Sopenharmony_ci case DSO__TYPE_UNKNOWN: 2828c2ecf20Sopenharmony_ci case DSO__TYPE_64BIT: 2838c2ecf20Sopenharmony_ci default: 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci#endif 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic struct dso *machine__find_vdso(struct machine *machine, 2918c2ecf20Sopenharmony_ci struct thread *thread) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct dso *dso = NULL; 2948c2ecf20Sopenharmony_ci enum dso_type dso_type; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci dso_type = machine__thread_dso_type(machine, thread); 2978c2ecf20Sopenharmony_ci switch (dso_type) { 2988c2ecf20Sopenharmony_ci case DSO__TYPE_32BIT: 2998c2ecf20Sopenharmony_ci dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO32, true); 3008c2ecf20Sopenharmony_ci if (!dso) { 3018c2ecf20Sopenharmony_ci dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, 3028c2ecf20Sopenharmony_ci true); 3038c2ecf20Sopenharmony_ci if (dso && dso_type != dso__type(dso, machine)) 3048c2ecf20Sopenharmony_ci dso = NULL; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case DSO__TYPE_X32BIT: 3088c2ecf20Sopenharmony_ci dso = __dsos__find(&machine->dsos, DSO__NAME_VDSOX32, true); 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci case DSO__TYPE_64BIT: 3118c2ecf20Sopenharmony_ci case DSO__TYPE_UNKNOWN: 3128c2ecf20Sopenharmony_ci default: 3138c2ecf20Sopenharmony_ci dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true); 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return dso; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistruct dso *machine__findnew_vdso(struct machine *machine, 3218c2ecf20Sopenharmony_ci struct thread *thread) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct vdso_info *vdso_info; 3248c2ecf20Sopenharmony_ci struct dso *dso = NULL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci down_write(&machine->dsos.lock); 3278c2ecf20Sopenharmony_ci if (!machine->vdso_info) 3288c2ecf20Sopenharmony_ci machine->vdso_info = vdso_info__new(); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci vdso_info = machine->vdso_info; 3318c2ecf20Sopenharmony_ci if (!vdso_info) 3328c2ecf20Sopenharmony_ci goto out_unlock; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci dso = machine__find_vdso(machine, thread); 3358c2ecf20Sopenharmony_ci if (dso) 3368c2ecf20Sopenharmony_ci goto out_unlock; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 64 3398c2ecf20Sopenharmony_ci if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso)) 3408c2ecf20Sopenharmony_ci goto out_unlock; 3418c2ecf20Sopenharmony_ci#endif 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true); 3448c2ecf20Sopenharmony_ci if (!dso) { 3458c2ecf20Sopenharmony_ci char *file; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci file = get_file(&vdso_info->vdso); 3488c2ecf20Sopenharmony_ci if (file) 3498c2ecf20Sopenharmony_ci dso = __machine__addnew_vdso(machine, DSO__NAME_VDSO, file); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ciout_unlock: 3538c2ecf20Sopenharmony_ci dso__get(dso); 3548c2ecf20Sopenharmony_ci up_write(&machine->dsos.lock); 3558c2ecf20Sopenharmony_ci return dso; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cibool dso__is_vdso(struct dso *dso) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci return !strcmp(dso->short_name, DSO__NAME_VDSO) || 3618c2ecf20Sopenharmony_ci !strcmp(dso->short_name, DSO__NAME_VDSO32) || 3628c2ecf20Sopenharmony_ci !strcmp(dso->short_name, DSO__NAME_VDSOX32); 3638c2ecf20Sopenharmony_ci} 364