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