Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v5.8 361 lines 7.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2#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 for 25 * 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 == 64 42 struct vdso_file vdso32; 43 struct vdso_file vdsox32; 44#endif 45}; 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 == 64 55 .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#endif 66 }; 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 == 64 118 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#endif 123 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 141static enum dso_type machine__thread_dso_type(struct machine *machine, 142 struct thread *thread) 143{ 144 enum dso_type dso_type = DSO__TYPE_UNKNOWN; 145 struct map *map; 146 147 maps__for_each_entry(thread->maps, map) { 148 struct dso *dso = map->dso; 149 if (!dso || dso->long_name[0] != '/') 150 continue; 151 dso_type = dso__type(dso, machine); 152 if (dso_type != DSO__TYPE_UNKNOWN) 153 break; 154 } 155 156 return dso_type; 157} 158 159#if BITS_PER_LONG == 64 160 161static int vdso__do_copy_compat(FILE *f, int fd) 162{ 163 char buf[4096]; 164 size_t count; 165 166 while (1) { 167 count = fread(buf, 1, sizeof(buf), f); 168 if (ferror(f)) 169 return -errno; 170 if (feof(f)) 171 break; 172 if (count && writen(fd, buf, count) != (ssize_t)count) 173 return -errno; 174 } 175 176 return 0; 177} 178 179static int vdso__copy_compat(const char *prog, int fd) 180{ 181 FILE *f; 182 int err; 183 184 f = popen(prog, "r"); 185 if (!f) 186 return -errno; 187 188 err = vdso__do_copy_compat(f, fd); 189 190 if (pclose(f) == -1) 191 return -errno; 192 193 return err; 194} 195 196static int vdso__create_compat_file(const char *prog, char *temp_name) 197{ 198 int fd, err; 199 200 fd = mkstemp(temp_name); 201 if (fd < 0) 202 return -errno; 203 204 err = vdso__copy_compat(prog, fd); 205 206 if (close(fd) == -1) 207 return -errno; 208 209 return err; 210} 211 212static const char *vdso__get_compat_file(struct vdso_file *vdso_file) 213{ 214 int err; 215 216 if (vdso_file->found) 217 return vdso_file->temp_file_name; 218 219 if (vdso_file->error) 220 return NULL; 221 222 err = vdso__create_compat_file(vdso_file->read_prog, 223 vdso_file->temp_file_name); 224 if (err) { 225 pr_err("%s failed, error %d\n", vdso_file->read_prog, err); 226 vdso_file->error = true; 227 return NULL; 228 } 229 230 vdso_file->found = true; 231 232 return vdso_file->temp_file_name; 233} 234 235static struct dso *__machine__findnew_compat(struct machine *machine, 236 struct vdso_file *vdso_file) 237{ 238 const char *file_name; 239 struct dso *dso; 240 241 dso = __dsos__find(&machine->dsos, vdso_file->dso_name, true); 242 if (dso) 243 goto out; 244 245 file_name = vdso__get_compat_file(vdso_file); 246 if (!file_name) 247 goto out; 248 249 dso = __machine__addnew_vdso(machine, vdso_file->dso_name, file_name); 250out: 251 return dso; 252} 253 254static int __machine__findnew_vdso_compat(struct machine *machine, 255 struct thread *thread, 256 struct vdso_info *vdso_info, 257 struct dso **dso) 258{ 259 enum dso_type dso_type; 260 261 dso_type = machine__thread_dso_type(machine, thread); 262 263#ifndef HAVE_PERF_READ_VDSO32 264 if (dso_type == DSO__TYPE_32BIT) 265 return 0; 266#endif 267#ifndef HAVE_PERF_READ_VDSOX32 268 if (dso_type == DSO__TYPE_X32BIT) 269 return 0; 270#endif 271 272 switch (dso_type) { 273 case DSO__TYPE_32BIT: 274 *dso = __machine__findnew_compat(machine, &vdso_info->vdso32); 275 return 1; 276 case DSO__TYPE_X32BIT: 277 *dso = __machine__findnew_compat(machine, &vdso_info->vdsox32); 278 return 1; 279 case DSO__TYPE_UNKNOWN: 280 case DSO__TYPE_64BIT: 281 default: 282 return 0; 283 } 284} 285 286#endif 287 288static struct dso *machine__find_vdso(struct machine *machine, 289 struct thread *thread) 290{ 291 struct dso *dso = NULL; 292 enum dso_type dso_type; 293 294 dso_type = machine__thread_dso_type(machine, thread); 295 switch (dso_type) { 296 case DSO__TYPE_32BIT: 297 dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO32, true); 298 if (!dso) { 299 dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, 300 true); 301 if (dso && dso_type != dso__type(dso, machine)) 302 dso = NULL; 303 } 304 break; 305 case DSO__TYPE_X32BIT: 306 dso = __dsos__find(&machine->dsos, DSO__NAME_VDSOX32, true); 307 break; 308 case DSO__TYPE_64BIT: 309 case DSO__TYPE_UNKNOWN: 310 default: 311 dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true); 312 break; 313 } 314 315 return dso; 316} 317 318struct dso *machine__findnew_vdso(struct machine *machine, 319 struct thread *thread) 320{ 321 struct vdso_info *vdso_info; 322 struct dso *dso = NULL; 323 324 down_write(&machine->dsos.lock); 325 if (!machine->vdso_info) 326 machine->vdso_info = vdso_info__new(); 327 328 vdso_info = machine->vdso_info; 329 if (!vdso_info) 330 goto out_unlock; 331 332 dso = machine__find_vdso(machine, thread); 333 if (dso) 334 goto out_unlock; 335 336#if BITS_PER_LONG == 64 337 if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso)) 338 goto out_unlock; 339#endif 340 341 dso = __dsos__find(&machine->dsos, DSO__NAME_VDSO, true); 342 if (!dso) { 343 char *file; 344 345 file = get_file(&vdso_info->vdso); 346 if (file) 347 dso = __machine__addnew_vdso(machine, DSO__NAME_VDSO, file); 348 } 349 350out_unlock: 351 dso__get(dso); 352 up_write(&machine->dsos.lock); 353 return dso; 354} 355 356bool dso__is_vdso(struct dso *dso) 357{ 358 return !strcmp(dso->short_name, DSO__NAME_VDSO) || 359 !strcmp(dso->short_name, DSO__NAME_VDSO32) || 360 !strcmp(dso->short_name, DSO__NAME_VDSOX32); 361}