370 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <errno.h>3#include <unistd.h>4#include <stdio.h>5#include <string.h>6#include <sys/types.h>7#include <sys/stat.h>8#include <fcntl.h>9#include <stdlib.h>10#include <linux/kernel.h>11 12#include "vdso.h"13#include "dso.h"14#include <internal/lib.h>15#include "map.h"16#include "symbol.h"17#include "machine.h"18#include "thread.h"19#include "linux/string.h"20#include <linux/zalloc.h>21#include "debug.h"22 23/*24 * Include definition of find_map() also used in perf-read-vdso.c for25 * building perf-read-vdso32 and perf-read-vdsox32.26 */27#include "find-map.c"28 29#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"30 31struct vdso_file {32 bool found;33 bool error;34 char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];35 const char *dso_name;36 const char *read_prog;37};38 39struct vdso_info {40 struct vdso_file vdso;41#if BITS_PER_LONG == 6442 struct vdso_file vdso32;43 struct vdso_file vdsox32;44#endif45};46 47static struct vdso_info *vdso_info__new(void)48{49 static const struct vdso_info vdso_info_init = {50 .vdso = {51 .temp_file_name = VDSO__TEMP_FILE_NAME,52 .dso_name = DSO__NAME_VDSO,53 },54#if BITS_PER_LONG == 6455 .vdso32 = {56 .temp_file_name = VDSO__TEMP_FILE_NAME,57 .dso_name = DSO__NAME_VDSO32,58 .read_prog = "perf-read-vdso32",59 },60 .vdsox32 = {61 .temp_file_name = VDSO__TEMP_FILE_NAME,62 .dso_name = DSO__NAME_VDSOX32,63 .read_prog = "perf-read-vdsox32",64 },65#endif66 };67 68 return memdup(&vdso_info_init, sizeof(vdso_info_init));69}70 71static char *get_file(struct vdso_file *vdso_file)72{73 char *vdso = NULL;74 char *buf = NULL;75 void *start, *end;76 size_t size;77 int fd;78 79 if (vdso_file->found)80 return vdso_file->temp_file_name;81 82 if (vdso_file->error || find_map(&start, &end, VDSO__MAP_NAME))83 return NULL;84 85 size = end - start;86 87 buf = memdup(start, size);88 if (!buf)89 return NULL;90 91 fd = mkstemp(vdso_file->temp_file_name);92 if (fd < 0)93 goto out;94 95 if (size == (size_t) write(fd, buf, size))96 vdso = vdso_file->temp_file_name;97 98 close(fd);99 100 out:101 free(buf);102 103 vdso_file->found = (vdso != NULL);104 vdso_file->error = !vdso_file->found;105 return vdso;106}107 108void machine__exit_vdso(struct machine *machine)109{110 struct vdso_info *vdso_info = machine->vdso_info;111 112 if (!vdso_info)113 return;114 115 if (vdso_info->vdso.found)116 unlink(vdso_info->vdso.temp_file_name);117#if BITS_PER_LONG == 64118 if (vdso_info->vdso32.found)119 unlink(vdso_info->vdso32.temp_file_name);120 if (vdso_info->vdsox32.found)121 unlink(vdso_info->vdsox32.temp_file_name);122#endif123 124 zfree(&machine->vdso_info);125}126 127static struct dso *__machine__addnew_vdso(struct machine *machine, const char *short_name,128 const char *long_name)129{130 struct dso *dso;131 132 dso = dso__new(short_name);133 if (dso != NULL) {134 __dsos__add(&machine->dsos, dso);135 dso__set_long_name(dso, long_name, false);136 }137 138 return dso;139}140 141struct machine__thread_dso_type_maps_cb_args {142 struct machine *machine;143 enum dso_type dso_type;144};145 146static int machine__thread_dso_type_maps_cb(struct map *map, void *data)147{148 struct machine__thread_dso_type_maps_cb_args *args = data;149 struct dso *dso = map__dso(map);150 151 if (!dso || dso__long_name(dso)[0] != '/')152 return 0;153 154 args->dso_type = dso__type(dso, args->machine);155 return (args->dso_type != DSO__TYPE_UNKNOWN) ? 1 : 0;156}157 158static enum dso_type machine__thread_dso_type(struct machine *machine,159 struct thread *thread)160{161 struct machine__thread_dso_type_maps_cb_args args = {162 .machine = machine,163 .dso_type = DSO__TYPE_UNKNOWN,164 };165 166 maps__for_each_map(thread__maps(thread), machine__thread_dso_type_maps_cb, &args);167 168 return args.dso_type;169}170 171#if BITS_PER_LONG == 64172 173static int vdso__do_copy_compat(FILE *f, int fd)174{175 char buf[4096];176 size_t count;177 178 while (1) {179 count = fread(buf, 1, sizeof(buf), f);180 if (ferror(f))181 return -errno;182 if (feof(f))183 break;184 if (count && writen(fd, buf, count) != (ssize_t)count)185 return -errno;186 }187 188 return 0;189}190 191static int vdso__copy_compat(const char *prog, int fd)192{193 FILE *f;194 int err;195 196 f = popen(prog, "r");197 if (!f)198 return -errno;199 200 err = vdso__do_copy_compat(f, fd);201 202 if (pclose(f) == -1)203 return -errno;204 205 return err;206}207 208static int vdso__create_compat_file(const char *prog, char *temp_name)209{210 int fd, err;211 212 fd = mkstemp(temp_name);213 if (fd < 0)214 return -errno;215 216 err = vdso__copy_compat(prog, fd);217 218 if (close(fd) == -1)219 return -errno;220 221 return err;222}223 224static const char *vdso__get_compat_file(struct vdso_file *vdso_file)225{226 int err;227 228 if (vdso_file->found)229 return vdso_file->temp_file_name;230 231 if (vdso_file->error)232 return NULL;233 234 err = vdso__create_compat_file(vdso_file->read_prog,235 vdso_file->temp_file_name);236 if (err) {237 pr_err("%s failed, error %d\n", vdso_file->read_prog, err);238 vdso_file->error = true;239 return NULL;240 }241 242 vdso_file->found = true;243 244 return vdso_file->temp_file_name;245}246 247static struct dso *__machine__findnew_compat(struct machine *machine,248 struct vdso_file *vdso_file)249{250 const char *file_name;251 struct dso *dso;252 253 dso = dsos__find(&machine->dsos, vdso_file->dso_name, true);254 if (dso)255 return dso;256 257 file_name = vdso__get_compat_file(vdso_file);258 if (!file_name)259 return NULL;260 261 return __machine__addnew_vdso(machine, vdso_file->dso_name, file_name);262}263 264static int __machine__findnew_vdso_compat(struct machine *machine,265 struct thread *thread,266 struct vdso_info *vdso_info,267 struct dso **dso)268{269 enum dso_type dso_type;270 271 dso_type = machine__thread_dso_type(machine, thread);272 273#ifndef HAVE_PERF_READ_VDSO32274 if (dso_type == DSO__TYPE_32BIT)275 return 0;276#endif277#ifndef HAVE_PERF_READ_VDSOX32278 if (dso_type == DSO__TYPE_X32BIT)279 return 0;280#endif281 282 switch (dso_type) {283 case DSO__TYPE_32BIT:284 *dso = __machine__findnew_compat(machine, &vdso_info->vdso32);285 return 1;286 case DSO__TYPE_X32BIT:287 *dso = __machine__findnew_compat(machine, &vdso_info->vdsox32);288 return 1;289 case DSO__TYPE_UNKNOWN:290 case DSO__TYPE_64BIT:291 default:292 return 0;293 }294}295 296#endif297 298static struct dso *machine__find_vdso(struct machine *machine,299 struct thread *thread)300{301 struct dso *dso = NULL;302 enum dso_type dso_type;303 304 dso_type = machine__thread_dso_type(machine, thread);305 switch (dso_type) {306 case DSO__TYPE_32BIT:307 dso = dsos__find(&machine->dsos, DSO__NAME_VDSO32, true);308 if (!dso) {309 dso = dsos__find(&machine->dsos, DSO__NAME_VDSO,310 true);311 if (dso && dso_type != dso__type(dso, machine)) {312 dso__put(dso);313 dso = NULL;314 }315 }316 break;317 case DSO__TYPE_X32BIT:318 dso = dsos__find(&machine->dsos, DSO__NAME_VDSOX32, true);319 break;320 case DSO__TYPE_64BIT:321 case DSO__TYPE_UNKNOWN:322 default:323 dso = dsos__find(&machine->dsos, DSO__NAME_VDSO, true);324 break;325 }326 327 return dso;328}329 330struct dso *machine__findnew_vdso(struct machine *machine,331 struct thread *thread)332{333 struct vdso_info *vdso_info;334 struct dso *dso = NULL;335 char *file;336 337 if (!machine->vdso_info)338 machine->vdso_info = vdso_info__new();339 340 vdso_info = machine->vdso_info;341 if (!vdso_info)342 return NULL;343 344 dso = machine__find_vdso(machine, thread);345 if (dso)346 return dso;347 348#if BITS_PER_LONG == 64349 if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso))350 return dso;351#endif352 353 dso = dsos__find(&machine->dsos, DSO__NAME_VDSO, true);354 if (dso)355 return dso;356 357 file = get_file(&vdso_info->vdso);358 if (!file)359 return NULL;360 361 return __machine__addnew_vdso(machine, DSO__NAME_VDSO, file);362}363 364bool dso__is_vdso(struct dso *dso)365{366 return !strcmp(dso__short_name(dso), DSO__NAME_VDSO) ||367 !strcmp(dso__short_name(dso), DSO__NAME_VDSO32) ||368 !strcmp(dso__short_name(dso), DSO__NAME_VDSOX32);369}370