]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
perf env, dso, thread: Add _endian variants for e_machine helpers
authorIan Rogers <irogers@google.com>
Tue, 2 Jun 2026 15:25:00 +0000 (08:25 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 3 Jun 2026 19:45:21 +0000 (16:45 -0300)
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>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Alexander Gordeev <agordeev@linux.ibm.com>
Cc: Heiko Carstens <hca@linux.ibm.com>
Cc: Honglei Wang <jameshongleiwang@126.com>
Cc: Jan Polensky <japo@linux.ibm.com>
Cc: Sumanth Korikkar <sumanthk@linux.ibm.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/dso.c
tools/perf/util/dso.h
tools/perf/util/env.c
tools/perf/util/env.h
tools/perf/util/thread.c
tools/perf/util/thread.h

index b791e1b6b2cf01f47d23d468b44e154fca841ced..7dced896c64eafd763142e06d6d9cfd25bfcdea2 100644 (file)
@@ -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 ? perf_env__arch(machine->env) : 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;
 
index ede691e9a24959d9baff589defc9a73e1a33cde0..2916b954a804b614cee99969175bdcc232fc1e74 100644 (file)
@@ -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);
index 0cd23b5fc65155ba46605c8b68a11ecf51a12e62..fae70b07ba8dea90df5b7a7c7513559f3d6a1dc5 100644 (file)
@@ -342,6 +342,22 @@ 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") ||
+           !strcmp(arch, "ppc") || !strcmp(arch, "ppc64") ||
+           !strcmp(arch, "mips") || !strcmp(arch, "mips64") || !strcmp(arch, "parisc") ||
+           !strcmp(arch, "parisc64") || !strcmp(arch, "m68k") ||
+           !strcmp(arch, "armeb") || !strcmp(arch, "mipseb") || !strcmp(arch, "mips64eb"))
+               return true;
+
+       return false;
+}
+
 int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
 {
        int i;
index bde192fd5be59fc747135b27aaacb175b3a38377..dd9907dbc3450a0e85808c578a4232a5a13ce874 100644 (file)
@@ -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[]);
 
index aac9cb75dcf44e4d1f7cd896fa0fa14cc3cb9e70..ba33c0dfc18fe2427f1f8bbd6b001aea65df8ed8 100644 (file)
@@ -56,6 +56,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
                thread__set_cpu(thread, -1);
                thread__set_guest_cpu(thread, -1);
                thread__set_e_machine(thread, EM_NONE);
+               thread__set_e_is_big_endian(thread, false);
                thread__set_lbr_stitch_enable(thread, false);
                INIT_LIST_HEAD(thread__namespaces_list(thread));
                INIT_LIST_HEAD(thread__comm_list(thread));
@@ -429,7 +430,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;
@@ -438,7 +439,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;
@@ -448,6 +450,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)
@@ -458,24 +461,38 @@ 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;
+       uint16_t e_machine;
        uint32_t local_e_flags = 0;
-       struct thread__e_machine_callback_args args = {
-               .machine = machine,
-               .e_flags = 0,
-               .e_machine = EM_NONE,
-       };
+       struct thread__e_machine_callback_args args;
+
+       if (!thread) {
+               if (is_big_endian) {
+                       *is_big_endian = perf_arch_is_big_endian(
+                               machine && machine->env ? perf_env__arch(machine->env) : NULL);
+               }
+               return perf_env__e_machine(machine ? machine->env : NULL, e_flags);
+       }
+
+       e_machine = RC_CHK_ACCESS(thread)->e_machine;
+       args.machine = machine;
+       args.e_flags = 0;
+       args.e_machine = EM_NONE;
+       args.is_big_endian = false;
 
        if (e_machine != EM_NONE) {
                if (e_flags)
                        *e_flags = thread__e_flags(thread);
+               if (is_big_endian)
+                       *is_big_endian = thread__e_is_big_endian(thread);
                return e_machine;
        }
 
@@ -483,6 +500,7 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3
                struct maps *maps = thread__maps(thread);
 
                machine = maps__machine(maps);
+               args.machine = machine;
        }
        tid = thread__tid(thread);
        pid = thread__pid(thread);
@@ -490,7 +508,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,
+                                                            &args.is_big_endian);
                        thread__put(parent);
                        goto out;
                }
@@ -515,16 +534,27 @@ uint16_t thread__e_machine(struct thread *thread, struct machine *machine, uint3
                        is_live = !!session->data;
                }
                /* Read from /proc/pid/exe if live. */
-               if (is_live)
-                       e_machine = read_proc_e_machine_for_pid(pid, &local_e_flags);
+               if (is_live) {
+                       e_machine = read_proc_e_machine_for_pid(pid, &local_e_flags,
+                                                               &args.is_big_endian);
+               } else if (machine && machine->env) {
+                       /* Offline analysis: fallback to environment metadata. */
+                       e_machine = perf_env__e_machine(machine->env, &local_e_flags);
+                       args.is_big_endian = perf_arch_is_big_endian(perf_env__arch(machine->env));
+               }
        }
 out:
        if (e_machine != EM_NONE) {
-               thread__set_e_machine(thread, e_machine);
                thread__set_e_flags(thread, local_e_flags);
+               thread__set_e_is_big_endian(thread, args.is_big_endian);
+               thread__set_e_machine(thread, e_machine);
+               if (is_big_endian)
+                       *is_big_endian = args.is_big_endian;
        } 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;
index f5792d3e8a16f1ce21477df8924361b832616319..d82fce8173aefedc0d9bf57dc4a03604185936d5 100644 (file)
@@ -69,6 +69,11 @@ DECLARE_RC_STRUCT(thread) {
         * computed.
         */
        uint16_t                e_machine;
+       /**
+        * @e_is_big_endian: True if the ELF architecture of the thread is big endian.
+        * Valid if e_machine != EM_NONE.
+        */
+       bool                    e_is_big_endian;
        /* LBR call stack stitch */
        bool                    lbr_stitch_enable;
        struct lbr_stitch       *lbr_stitch;
@@ -311,7 +316,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)
 {
@@ -328,6 +339,16 @@ static inline void thread__set_e_flags(struct thread *thread, uint32_t e_flags)
        RC_CHK_ACCESS(thread)->e_flags = e_flags;
 }
 
+static inline bool thread__e_is_big_endian(const struct thread *thread)
+{
+       return RC_CHK_ACCESS(thread)->e_is_big_endian;
+}
+
+static inline void thread__set_e_is_big_endian(struct thread *thread, bool is_big_endian)
+{
+       RC_CHK_ACCESS(thread)->e_is_big_endian = is_big_endian;
+}
+
 
 static inline bool thread__lbr_stitch_enable(const struct thread *thread)
 {