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