// SPDX-License-Identifier: GPL-2.0-only
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <inttypes.h>
 #include <libgen.h>
 #include <regex.h>
 #include "evsel.h"
 #include "map.h"
 #include "maps.h"
+#include "namespaces.h"
 #include "srcline.h"
 #include "symbol.h"
 #include "util.h"
        return 0;
 }
 
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+#include <capstone/capstone.h>
+
+static int open_capstone_handle(struct annotate_args *args, bool is_64bit,
+                               csh *handle)
+{
+       struct annotation_options *opt = args->options;
+       cs_mode mode = is_64bit ? CS_MODE_64 : CS_MODE_32;
+
+       /* TODO: support more architectures */
+       if (!arch__is(args->arch, "x86"))
+               return -1;
+
+       if (cs_open(CS_ARCH_X86, mode, handle) != CS_ERR_OK)
+               return -1;
+
+       if (!opt->disassembler_style ||
+           !strcmp(opt->disassembler_style, "att"))
+               cs_option(*handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
+
+       return 0;
+}
+
+struct find_file_offset_data {
+       u64 ip;
+       u64 offset;
+};
+
+/* This will be called for each PHDR in an ELF binary */
+static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg)
+{
+       struct find_file_offset_data *data = arg;
+
+       if (start <= data->ip && data->ip < start + len) {
+               data->offset = pgoff + data->ip - start;
+               return 1;
+       }
+       return 0;
+}
+
+static int symbol__disassemble_capstone(char *filename, struct symbol *sym,
+                                       struct annotate_args *args)
+{
+       struct annotation *notes = symbol__annotation(sym);
+       struct map *map = args->ms.map;
+       struct dso *dso = map__dso(map);
+       struct nscookie nsc;
+       u64 start = map__rip_2objdump(map, sym->start);
+       u64 end = map__rip_2objdump(map, sym->end);
+       u64 len = end - start;
+       u64 offset;
+       int i, fd, count;
+       bool is_64bit = false;
+       bool needs_cs_close = false;
+       u8 *buf = NULL;
+       struct find_file_offset_data data = {
+               .ip = start,
+       };
+       csh handle;
+       cs_insn *insn;
+       char disasm_buf[512];
+       struct disasm_line *dl;
+
+       if (args->options->objdump_path)
+               return -1;
+
+       nsinfo__mountns_enter(dso->nsinfo, &nsc);
+       fd = open(filename, O_RDONLY);
+       nsinfo__mountns_exit(&nsc);
+       if (fd < 0)
+               return -1;
+
+       if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data,
+                           &is_64bit) == 0)
+               goto err;
+
+       if (open_capstone_handle(args, is_64bit, &handle) < 0)
+               goto err;
+
+       needs_cs_close = true;
+
+       buf = malloc(len);
+       if (buf == NULL)
+               goto err;
+
+       count = pread(fd, buf, len, data.offset);
+       close(fd);
+       fd = -1;
+
+       if ((u64)count != len)
+               goto err;
+
+       /* add the function address and name */
+       scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:",
+                 start, sym->name);
+
+       args->offset = -1;
+       args->line = disasm_buf;
+       args->line_nr = 0;
+       args->fileloc = NULL;
+       args->ms.sym = sym;
+
+       dl = disasm_line__new(args);
+       if (dl == NULL)
+               goto err;
+
+       annotation_line__add(&dl->al, ¬es->src->source);
+
+       count = cs_disasm(handle, buf, len, start, len, &insn);
+       for (i = 0, offset = 0; i < count; i++) {
+               scnprintf(disasm_buf, sizeof(disasm_buf),
+                         "       %-7s %s",
+                         insn[i].mnemonic, insn[i].op_str);
+
+               args->offset = offset;
+               args->line = disasm_buf;
+
+               dl = disasm_line__new(args);
+               if (dl == NULL)
+                       goto err;
+
+               annotation_line__add(&dl->al, ¬es->src->source);
+
+               offset += insn[i].size;
+       }
+
+out:
+       if (needs_cs_close)
+               cs_close(&handle);
+       free(buf);
+       return count < 0 ? count : 0;
+
+err:
+       if (fd >= 0)
+               close(fd);
+       if (needs_cs_close) {
+               struct disasm_line *tmp;
+
+               /*
+                * It probably failed in the middle of the above loop.
+                * Release any resources it might add.
+                */
+               list_for_each_entry_safe(dl, tmp, ¬es->src->source, al.node) {
+                       list_del(&dl->al.node);
+                       free(dl);
+               }
+       }
+       count = -1;
+       goto out;
+}
+#endif
+
 /*
  * Possibly create a new version of line with tabs expanded. Returns the
  * existing or new line, storage is updated if a new line is allocated. If
                strcpy(symfs_filename, tmp);
        }
 
+#ifdef HAVE_LIBCAPSTONE_SUPPORT
+       err = symbol__disassemble_capstone(symfs_filename, sym, args);
+       if (err == 0)
+               goto out_remove_tmp;
+#endif
+
        err = asprintf(&command,
                 "%s %s%s --start-address=0x%016" PRIx64
                 " --stop-address=0x%016" PRIx64