diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 3b272a6fae24..91c9f7cb9d8c 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1203,6 +1203,68 @@ ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, return data_read_write_offset(dso, machine, offset, data, size, true); } +static enum dso_swap_type dso_swap_type__from_elf_data(unsigned char eidata) +{ + static const unsigned int endian = 1; + + switch (eidata) { + case ELFDATA2LSB: + /* We are big endian, DSO is little endian. */ + return (*(unsigned char const *)&endian != 1) ? DSO_SWAP__YES : DSO_SWAP__NO; + case ELFDATA2MSB: + /* We are little endian, DSO is big endian. */ + return (*(unsigned char const *)&endian != 0) ? DSO_SWAP__YES : DSO_SWAP__NO; + default: + return DSO_SWAP__UNSET; + } +} + +/* Reads e_machine from fd, optionally caching data in dso. */ +uint16_t dso__read_e_machine(struct dso *optional_dso, int fd) +{ + uint16_t e_machine = EM_NONE; + unsigned char e_ident[EI_NIDENT]; + enum dso_swap_type swap_type; + + _Static_assert(offsetof(Elf32_Ehdr, e_ident) == 0, "Unexpected offset"); + _Static_assert(offsetof(Elf64_Ehdr, e_ident) == 0, "Unexpected offset"); + if (pread(fd, &e_ident, sizeof(e_ident), 0) != sizeof(e_ident)) + return EM_NONE; // Read failed. + + if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) + return EM_NONE; // Not an ELF file. + + if (e_ident[EI_CLASS] == ELFCLASSNONE || e_ident[EI_CLASS] >= ELFCLASSNUM) + return EM_NONE; // Bad ELF class (32 or 64-bit objects). + + if (e_ident[EI_VERSION] != EV_CURRENT) + return EM_NONE; // Bad ELF version. + + swap_type = dso_swap_type__from_elf_data(e_ident[EI_DATA]); + if (swap_type == DSO_SWAP__UNSET) + return EM_NONE; // Bad ELF data encoding. + + /* Cache the need for swapping. */ + if (optional_dso) { + assert(dso__needs_swap(optional_dso) == DSO_SWAP__UNSET || + dso__needs_swap(optional_dso) == swap_type); + dso__set_needs_swap(optional_dso, swap_type); + } + + { + _Static_assert(offsetof(Elf32_Ehdr, e_machine) == 18, "Unexpected offset"); + _Static_assert(offsetof(Elf64_Ehdr, e_machine) == 18, "Unexpected offset"); + if (pread(fd, &e_machine, sizeof(e_machine), 18) != sizeof(e_machine)) + return EM_NONE; // e_machine read failed. + } + + e_machine = DSO_SWAP_TYPE__SWAP(swap_type, uint16_t, e_machine); + if (e_machine >= EM_NUM) + return EM_NONE; // Bad ELF machine number. + + return e_machine; +} + uint16_t dso__e_machine(struct dso *dso, struct machine *machine) { uint16_t e_machine = EM_NONE; @@ -1248,30 +1310,9 @@ uint16_t dso__e_machine(struct dso *dso, struct machine *machine) */ try_to_open_dso(dso, machine); fd = dso__data(dso)->fd; - if (fd >= 0) { - unsigned char e_ident[EI_NIDENT]; + if (fd >= 0) + e_machine = dso__read_e_machine(dso, fd); - _Static_assert(offsetof(Elf32_Ehdr, e_ident) == 0, "Unexpected offset"); - _Static_assert(offsetof(Elf64_Ehdr, e_ident) == 0, "Unexpected offset"); - if (pread(fd, &e_ident, sizeof(e_ident), 0) == sizeof(e_ident) && - memcmp(e_ident, ELFMAG, SELFMAG) == 0 && - e_ident[EI_CLASS] > ELFCLASSNONE && e_ident[EI_CLASS] < ELFCLASSNUM && - e_ident[EI_DATA] > ELFDATANONE && e_ident[EI_DATA] < ELFDATANUM && - e_ident[EI_VERSION] == EV_CURRENT) { - _Static_assert(offsetof(Elf32_Ehdr, e_machine) == 18, "Unexpected offset"); - _Static_assert(offsetof(Elf64_Ehdr, e_machine) == 18, "Unexpected offset"); - - if (dso__needs_swap(dso) == DSO_SWAP__UNSET) - dso__swap_init(dso, e_ident[EI_DATA]); - - if (dso__needs_swap(dso) != DSO_SWAP__UNSET && - pread(fd, &e_machine, sizeof(e_machine), 18) == sizeof(e_machine) && - e_machine < EM_NUM) - e_machine = DSO__SWAP(dso, uint16_t, e_machine); - else - e_machine = EM_NONE; - } - } mutex_unlock(dso__data_open_lock()); return e_machine; } @@ -1656,28 +1697,13 @@ void dso__put(struct dso *dso) int dso__swap_init(struct dso *dso, unsigned char eidata) { - static unsigned int const endian = 1; + enum dso_swap_type type = dso_swap_type__from_elf_data(eidata); - dso__set_needs_swap(dso, DSO_SWAP__NO); - - switch (eidata) { - case ELFDATA2LSB: - /* We are big endian, DSO is little endian. */ - if (*(unsigned char const *)&endian != 1) - dso__set_needs_swap(dso, DSO_SWAP__YES); - break; - - case ELFDATA2MSB: - /* We are little endian, DSO is big endian. */ - if (*(unsigned char const *)&endian != 0) - dso__set_needs_swap(dso, DSO_SWAP__YES); - break; - - default: + dso__set_needs_swap(dso, type); + if (type == DSO_SWAP__UNSET) { pr_err("unrecognized DSO data encoding %d\n", eidata); return -EINVAL; } - return 0; } diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index ac725bc8ea74..a95fee7d634b 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -160,12 +160,11 @@ enum dso_load_errno { __DSO_LOAD_ERRNO__END, }; -#define DSO__SWAP(dso, type, val) \ +#define DSO_SWAP_TYPE__SWAP(swap_type, type, val) \ ({ \ type ____r = val; \ - enum dso_swap_type ___dst = dso__needs_swap(dso); \ - BUG_ON(___dst == DSO_SWAP__UNSET); \ - if (___dst == DSO_SWAP__YES) { \ + BUG_ON(swap_type == DSO_SWAP__UNSET); \ + if (swap_type == DSO_SWAP__YES) { \ switch (sizeof(____r)) { \ case 2: \ ____r = bswap_16(val); \ @@ -183,6 +182,8 @@ enum dso_load_errno { ____r; \ }) +#define DSO__SWAP(dso, type, val) DSO_SWAP_TYPE__SWAP(dso__needs_swap(dso), type, val) + #define DSO__DATA_CACHE_SIZE 4096 #define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1) @@ -865,6 +866,7 @@ int dso__data_file_size(struct dso *dso, struct machine *machine); off_t dso__data_size(struct dso *dso, struct machine *machine); ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, u64 offset, u8 *data, ssize_t size); +uint16_t dso__read_e_machine(struct dso *optional_dso, int fd); uint16_t dso__e_machine(struct dso *dso, struct machine *machine); ssize_t dso__data_read_addr(struct dso *dso, struct map *map, struct machine *machine, u64 addr, diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index aa9c58bbf9d3..3642858e6cbc 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -458,10 +458,7 @@ static uint16_t read_proc_e_machine_for_pid(pid_t pid) snprintf(path, sizeof(path), "/proc/%d/exe", pid); fd = open(path, O_RDONLY); if (fd >= 0) { - _Static_assert(offsetof(Elf32_Ehdr, e_machine) == 18, "Unexpected offset"); - _Static_assert(offsetof(Elf64_Ehdr, e_machine) == 18, "Unexpected offset"); - if (pread(fd, &e_machine, sizeof(e_machine), 18) != sizeof(e_machine)) - e_machine = EM_NONE; + e_machine = dso__read_e_machine(/*optional_dso=*/NULL, fd); close(fd); } return e_machine;