[PATCH v9 03/18] perf env, dso, thread: Add _endian variants for e_machine helpers

Ian Rogers
2026-05-03T00:22:59Z

Add perf_arch_is_big_endian(), dso__read_e_machine_endian(),
dso__e_machine_endian(), and thread__e_machine_endian() to support
bi-endianness and cross-architecture analysis without breaking the
existing API.

These helpers allow querying the absolute endianness of a DSO or
thread, which is required for tools like Capstone that need to set the
correct disassembly mode.

Assisted-by: Gemini:gemini-3.1-pro-preview
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/dso.c | 19 +++++++++++++------
tools/perf/util/dso.h | 14 ++++++++++++--
tools/perf/util/env.c | 12 ++++++++++++
tools/perf/util/env.h | 1 +
tools/perf/util/thread.c | 22 ++++++++++++++++------
tools/perf/util/thread.h | 8 +++++++-
6 files changed, 61 insertions(+), 15 deletions(-)

diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index b791e1b6b2cf..6439b2a3c898 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1220,7 +1220,8 @@ static enum dso_swap_type dso_swap_type__from_elf_data(unsigned char eidata)
}

/* Reads e_machine from fd, optionally caching data in dso. */
-uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags)
+uint16_t dso__read_e_machine_endian(struct dso *optional_dso, int fd, uint32_t *e_flags,
+ bool *is_big_endian)
{
uint16_t e_machine = EM_NONE;
unsigned char e_ident[EI_NIDENT];
@@ -1250,6 +1251,9 @@ uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags
if (swap_type == DSO_SWAP__UNSET)
return EM_NONE; // Bad ELF data encoding.

+ if (is_big_endian)
+ *is_big_endian = (e_ident[EI_DATA] == ELFDATA2MSB);
+
/* Cache the need for swapping. */
if (optional_dso) {
assert(dso__needs_swap(optional_dso) == DSO_SWAP__UNSET ||
@@ -1288,7 +1292,8 @@ uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags
return e_machine;
}

-uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_flags)
+uint16_t dso__e_machine_endian(struct dso *dso, struct machine *machine, uint32_t *e_flags,
+ bool *is_big_endian)
{
uint16_t e_machine = EM_NONE;
int fd;
@@ -1308,9 +1313,11 @@ uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_fl
case DSO_BINARY_TYPE__BPF_IMAGE:
case DSO_BINARY_TYPE__OOL:
case DSO_BINARY_TYPE__JAVA_JIT:
- if (e_flags)
- *e_flags = EF_HOST;
- return EM_HOST;
+ if (is_big_endian) {
+ *is_big_endian = perf_arch_is_big_endian(
+ machine && machine->env ? machine->env->arch : NULL);
+ }
+ return perf_env__e_machine(machine ? machine->env : NULL, e_flags);
case DSO_BINARY_TYPE__DEBUGLINK:
case DSO_BINARY_TYPE__BUILD_ID_CACHE:
case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
@@ -1338,7 +1345,7 @@ uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_fl
try_to_open_dso(dso, machine);
fd = dso__data(dso)->fd;
if (fd >= 0)
- e_machine = dso__read_e_machine(dso, fd, e_flags);
+ e_machine = dso__read_e_machine_endian(dso, fd, e_flags, is_big_endian);
else if (e_flags)
*e_flags = 0;

diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index ede691e9a249..2916b954a804 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -866,8 +866,18 @@ 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, uint32_t *e_flags);
-uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_flags);
+uint16_t dso__read_e_machine_endian(struct dso *optional_dso, int fd, uint32_t *e_flags,
+ bool *is_big_endian);
+static inline uint16_t dso__read_e_machine(struct dso *optional_dso, int fd, uint32_t *e_flags)
+{
+ return dso__read_e_machine_endian(optional_dso, fd, e_flags, NULL);
+}
+uint16_t dso__e_machine_endian(struct dso *dso, struct machine *machine, uint32_t *e_flags,
+ bool *is_big_endian);
+static inline uint16_t dso__e_machine(struct dso *dso, struct machine *machine, uint32_t *e_flags)
+{
+ return dso__e_machine_endian(dso, machine, e_flags, NULL);
+}
ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
struct machine *machine, u64 addr,
u8 *data, ssize_t size);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 0edc67a468ab..1a4db133262b 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -339,6 +339,18 @@ int perf_env__kernel_is_64_bit(struct perf_env *env)
return env->kernel_is_64_bit;
}

+bool perf_arch_is_big_endian(const char *arch)
+{
+ if (!arch)
+ return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__;
+
+ if (str_ends_with(arch, "_be") || !strcmp(arch, "sparc") || !strcmp(arch, "sparc64") ||
+ !strcmp(arch, "s390") || !strcmp(arch, "s390x") || !strcmp(arch, "powerpc"))
+ return true;
+
+ return false;
+}
+
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
{
int i;
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 7151a9138e3f..c355df2dba7b 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -175,6 +175,7 @@ void free_cpu_domain_info(struct cpu_domain_map **cd_map, u32 schedstat_version,
void perf_env__exit(struct perf_env *env);

int perf_env__kernel_is_64_bit(struct perf_env *env);
+bool perf_arch_is_big_endian(const char *arch);

int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]);

diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 22be77225bb0..8611293deca9 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -449,7 +449,7 @@ void thread__find_cpumode_addr_location(struct thread *thread, u64 addr,
}
}

-static uint16_t read_proc_e_machine_for_pid(pid_t pid, uint32_t *e_flags)
+static uint16_t read_proc_e_machine_for_pid(pid_t pid, uint32_t *e_flags, bool *is_big_endian)
{
char path[6 /* "/proc/" */ + 11 /* max length of pid */ + 5 /* "/exe\0" */];
int fd;
@@ -458,7 +458,8 @@ static uint16_t read_proc_e_machine_for_pid(pid_t pid, uint32_t *e_flags)
snprintf(path, sizeof(path), "/proc/%d/exe", pid);
fd = open(path, O_RDONLY);
if (fd >= 0) {
- e_machine = dso__read_e_machine(/*optional_dso=*/NULL, fd, e_flags);
+ e_machine = dso__read_e_machine_endian(/*optional_dso=*/NULL, fd, e_flags,
+ is_big_endian);
close(fd);
}
return e_machine;
@@ -468,6 +469,7 @@ struct thread__e_machine_callback_args {
struct machine *machine;
uint32_t e_flags;
uint16_t e_machine;
+ bool is_big_endian;
};

static int thread__e_machine_callback(struct map *map, void *_args)
@@ -478,11 +480,13 @@ static int thread__e_machine_callback(struct map *map, void *_args)
if (!dso)
return 0; // No dso, continue search.

- args->e_machine = dso__e_machine(dso, args->machine, &args->e_flags);
+ args->e_machine =
+ dso__e_machine_endian(dso, args->machine, &args->e_flags, &args->is_big_endian);
return args->e_machine != EM_NONE ? 1 /* stop search */ : 0 /* continue search */;
}

-uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint32_t *e_flags)
+uint16_t thread__e_machine_endian(struct thread *thread, struct machine *machine, uint32_t *e_flags,
+ bool *is_big_endian)
{
pid_t tid, pid;
uint16_t e_machine = RC_CHK_ACCESS(thread)->e_machine;
@@ -491,6 +495,7 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3
.machine = machine,
.e_flags = 0,
.e_machine = EM_NONE,
+ .is_big_endian = false,
};

if (e_machine != EM_NONE) {
@@ -510,7 +515,8 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3
struct thread *parent = machine__findnew_thread(machine, pid, pid);

if (parent) {
- e_machine = thread__e_machine(parent, machine, &local_e_flags);
+ e_machine = thread__e_machine_endian(parent, machine, &local_e_flags,
+ is_big_endian);
thread__put(parent);
goto out;
}
@@ -522,6 +528,8 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3
if (args.e_machine != EM_NONE) {
e_machine = args.e_machine;
local_e_flags = args.e_flags;
+ if (is_big_endian)
+ *is_big_endian = args.is_big_endian;
} else {
/* Maps failed, perhaps we're live with map events disabled. */
bool is_live = machine->machines == NULL;
@@ -536,7 +544,7 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3
}
/* Read from /proc/pid/exe if live. */
if (is_live)
- e_machine = read_proc_e_machine_for_pid(pid, &local_e_flags);
+ e_machine = read_proc_e_machine_for_pid(pid, &local_e_flags, is_big_endian);
}
out:
if (e_machine != EM_NONE) {
@@ -545,6 +553,8 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3
} else {
e_machine = EM_HOST;
local_e_flags = EF_HOST;
+ if (is_big_endian)
+ *is_big_endian = (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
}
if (e_flags)
*e_flags = local_e_flags;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index f5792d3e8a16..8b58590c89de 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -311,7 +311,13 @@ static inline void thread__set_filter_entry_depth(struct thread *thread, int dep
RC_CHK_ACCESS(thread)->filter_entry_depth = depth;
}

-uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint32_t *e_flags);
+uint16_t thread__e_machine_endian(struct thread *thread, struct machine *machine, uint32_t *e_flags,
+ bool *is_big_endian);
+static inline uint16_t thread__e_machine(struct thread *thread, struct machine *machine,
+ uint32_t *e_flags)
+{
+ return thread__e_machine_endian(thread, machine, e_flags, NULL);
+}

static inline void thread__set_e_machine(struct thread *thread, uint16_t e_machine)
{
--
2.54.0.545.g6539524ca2-goog