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 31 struct 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 39 struct 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 47 static 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 71 static 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 108 void 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 127 static 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 141 struct machine__thread_dso_type_maps_cb_args { 142 struct machine *machine; 143 enum dso_type dso_type; 144 }; 145 146 static 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 158 static 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 == 64 172 173 static 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 191 static 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 208 static 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 224 static 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 247 static 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 264 static 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_VDSO32 274 if (dso_type == DSO__TYPE_32BIT) 275 return 0; 276 #endif 277 #ifndef HAVE_PERF_READ_VDSOX32 278 if (dso_type == DSO__TYPE_X32BIT) 279 return 0; 280 #endif 281 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 #endif 297 298 static 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 = NULL; 313 } 314 break; 315 case DSO__TYPE_X32BIT: 316 dso = dsos__find(&machine->dsos, DSO__NAME_VDSOX32, true); 317 break; 318 case DSO__TYPE_64BIT: 319 case DSO__TYPE_UNKNOWN: 320 default: 321 dso = dsos__find(&machine->dsos, DSO__NAME_VDSO, true); 322 break; 323 } 324 325 return dso; 326 } 327 328 struct dso *machine__findnew_vdso(struct machine *machine, 329 struct thread *thread) 330 { 331 struct vdso_info *vdso_info; 332 struct dso *dso = NULL; 333 char *file; 334 335 if (!machine->vdso_info) 336 machine->vdso_info = vdso_info__new(); 337 338 vdso_info = machine->vdso_info; 339 if (!vdso_info) 340 return NULL; 341 342 dso = machine__find_vdso(machine, thread); 343 if (dso) 344 return dso; 345 346 #if BITS_PER_LONG == 64 347 if (__machine__findnew_vdso_compat(machine, thread, vdso_info, &dso)) 348 return dso; 349 #endif 350 351 dso = dsos__find(&machine->dsos, DSO__NAME_VDSO, true); 352 if (dso) 353 return dso; 354 355 file = get_file(&vdso_info->vdso); 356 if (!file) 357 return NULL; 358 359 return __machine__addnew_vdso(machine, DSO__NAME_VDSO, file); 360 } 361 362 bool dso__is_vdso(struct dso *dso) 363 { 364 return !strcmp(dso__short_name(dso), DSO__NAME_VDSO) || 365 !strcmp(dso__short_name(dso), DSO__NAME_VDSO32) || 366 !strcmp(dso__short_name(dso), DSO__NAME_VDSOX32); 367 } 368