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 static 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 map_groups__for_each_entry(thread->mg, 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 161 static 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 179 static 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 196 static 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 212 static 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 235 static 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); 250 out: 251 return dso; 252 } 253 254 static 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 288 static 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 318 struct 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 350 out_unlock: 351 dso__get(dso); 352 up_write(&machine->dsos.lock); 353 return dso; 354 } 355 356 bool 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 } 362