mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
perf capstone: Symbolize address operands to match objdump on arm64
Capstone currently outputs address-bearing instructions with a raw hex value prefixed by '#', without symbolic resolution. This differs from objdump output and leads to parse failures in jump__parse() and arm64_mov__parse() on arm64. Example of the mismatch: Current: b #0xffff8000800114c8 Objdump: b ffff8000800114c8 <el0t_64_sync+0x108> Current: adrp x18, #0xffff800081f5f000 Objdump: adrp x18, ffff800081f5f000 <this_cpu_vector> Fix this by extending symbol__disassemble_capstone() to: - Enable CS_OPT_DETAIL for arm64 to access instruction group info. - Detect instructions with address operands requiring symbolization (jump/call/branch/adr/adrp) via opcode or Capstone group ID. - Resolve target addresses to symbols via find_symbol_from_addr(). - Format output to match objdump's style (strip '#' prefix, add symbol and offset). Signed-off-by: Tengda Wu <wutengda@huaweicloud.com> Reviewed-by: Namhyung Kim <namhyung@kernel.org> Reviewed-by: Ian Rogers <irogers@google.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
aaca646915
commit
268d5c3979
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
|
@ -31,6 +32,10 @@
|
|||
#define CS_MODE_RISCVC 4
|
||||
#endif
|
||||
|
||||
#if CS_VERSION_MAJOR < 4
|
||||
#define ARM64_GRP_BRANCH_RELATIVE 7 /* = CS_GRP_BRANCH_RELATIVE */
|
||||
#endif
|
||||
|
||||
#ifdef LIBCAPSTONE_DLOPEN
|
||||
static void *perf_cs_dll_handle(void)
|
||||
{
|
||||
|
|
@ -225,6 +230,12 @@ static int capstone_init(uint16_t e_machine, csh *cs_handle, bool is64, bool is_
|
|||
* on x86 by investigating instruction details.
|
||||
*/
|
||||
perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
|
||||
} else if (arch == CS_ARCH_ARM64) {
|
||||
/*
|
||||
* Same as x86: arm64 needs instruction details to resolve
|
||||
* symbolic addresses.
|
||||
*/
|
||||
perf_cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -292,6 +303,40 @@ ssize_t capstone__fprintf_insn_asm(struct machine *machine, struct thread *threa
|
|||
return printed;
|
||||
}
|
||||
|
||||
static struct symbol *find_symbol_from_addr(struct map *map, u64 addr,
|
||||
struct map **found_map, u64 *sym_offset)
|
||||
{
|
||||
struct symbol *sym;
|
||||
u64 al_addr;
|
||||
u64 ip = map__objdump_2mem(map, addr);
|
||||
|
||||
*found_map = NULL;
|
||||
if (dso__kernel(map__dso(map))) {
|
||||
/*
|
||||
* The kernel maps can be split into sections, let's
|
||||
* find the map first and then search the symbol.
|
||||
*/
|
||||
*found_map = maps__find(map__kmaps(map), ip);
|
||||
if (*found_map == NULL)
|
||||
return NULL;
|
||||
map = *found_map;
|
||||
}
|
||||
|
||||
/* convert it to map-relative address for search */
|
||||
al_addr = map__map_ip(map, ip);
|
||||
|
||||
sym = map__find_symbol(map, al_addr);
|
||||
|
||||
if (sym == NULL) {
|
||||
map__put(*found_map);
|
||||
*found_map = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*sym_offset = al_addr - sym->start;
|
||||
return sym;
|
||||
}
|
||||
|
||||
static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
|
||||
struct annotate_args *args, u64 addr)
|
||||
{
|
||||
|
|
@ -299,17 +344,14 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
|
|||
struct map *map = args->ms->map;
|
||||
struct symbol *sym;
|
||||
|
||||
/* TODO: support more architectures */
|
||||
if (!arch__is_x86(args->arch))
|
||||
return;
|
||||
|
||||
if (insn->detail == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < insn->detail->x86.op_count; i++) {
|
||||
struct cs_x86_op *op = &insn->detail->x86.operands[i];
|
||||
u64 orig_addr;
|
||||
struct map *found_map = NULL;
|
||||
struct map *found_map;
|
||||
u64 sym_offset;
|
||||
|
||||
if (op->type != X86_OP_MEM)
|
||||
continue;
|
||||
|
|
@ -320,40 +362,119 @@ static void print_capstone_detail(struct cs_insn *insn, char *buf, size_t len,
|
|||
|
||||
/* get the target address */
|
||||
orig_addr = addr + insn->size + op->mem.disp;
|
||||
addr = map__objdump_2mem(map, orig_addr);
|
||||
|
||||
if (dso__kernel(map__dso(map))) {
|
||||
/*
|
||||
* The kernel maps can be split into sections, let's
|
||||
* find the map first and then search the symbol.
|
||||
*/
|
||||
found_map = maps__find(map__kmaps(map), addr);
|
||||
if (found_map == NULL)
|
||||
continue;
|
||||
map = found_map;
|
||||
}
|
||||
|
||||
/* convert it to map-relative address for search */
|
||||
addr = map__map_ip(map, addr);
|
||||
|
||||
sym = map__find_symbol(map, addr);
|
||||
if (sym == NULL) {
|
||||
map__put(found_map);
|
||||
sym = find_symbol_from_addr(map, orig_addr, &found_map, &sym_offset);
|
||||
if (sym == NULL)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (addr == sym->start) {
|
||||
if (sym_offset == 0) {
|
||||
scnprintf(buf, len, "\t# %"PRIx64" <%s>",
|
||||
orig_addr, sym->name);
|
||||
} else {
|
||||
scnprintf(buf, len, "\t# %"PRIx64" <%s+%#"PRIx64">",
|
||||
orig_addr, sym->name, addr - sym->start);
|
||||
orig_addr, sym->name, sym_offset);
|
||||
}
|
||||
map__put(found_map);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int print_default_format(struct cs_insn *insn, char *buf, size_t len)
|
||||
{
|
||||
return scnprintf(buf, len, " %-7s %s",
|
||||
insn->mnemonic, insn->op_str);
|
||||
}
|
||||
|
||||
static void format_capstone_insn_x86(struct cs_insn *insn, char *buf,
|
||||
size_t len, struct annotate_args *args,
|
||||
u64 addr)
|
||||
{
|
||||
int printed;
|
||||
|
||||
printed = print_default_format(insn, buf, len);
|
||||
buf += printed;
|
||||
len -= printed;
|
||||
|
||||
print_capstone_detail(insn, buf, len, args, addr);
|
||||
}
|
||||
|
||||
static bool needs_symbolic_address(struct cs_insn *insn)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (insn->id == ARM64_INS_ADR || insn->id == ARM64_INS_ADRP)
|
||||
return true;
|
||||
|
||||
if (insn->detail == NULL)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < insn->detail->groups_count; i++) {
|
||||
if (insn->detail->groups[i] == ARM64_GRP_JUMP ||
|
||||
insn->detail->groups[i] == ARM64_GRP_CALL ||
|
||||
insn->detail->groups[i] == ARM64_GRP_BRANCH_RELATIVE)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void format_capstone_insn_arm64(struct cs_insn *insn, char *buf,
|
||||
size_t len, struct annotate_args *args)
|
||||
{
|
||||
struct map *map = args->ms->map;
|
||||
char *last_imm, *endptr;
|
||||
u64 addr;
|
||||
|
||||
print_default_format(insn, buf, len);
|
||||
/*
|
||||
* Adjust instructions to keep the existing behavior with objdump.
|
||||
*
|
||||
* Example conversion:
|
||||
* From: b #0xffff8000800114c8
|
||||
* To: b ffff8000800114c8 <el0t_64_sync+0x108>
|
||||
*/
|
||||
if (needs_symbolic_address(insn)) {
|
||||
struct symbol *sym;
|
||||
struct map *found_map;
|
||||
u64 sym_offset;
|
||||
|
||||
/* Extract last immediate value as address */
|
||||
last_imm = strrchr(buf, '#');
|
||||
if (!last_imm)
|
||||
return;
|
||||
|
||||
addr = strtoull(last_imm + 1, &endptr, 16);
|
||||
if (endptr == last_imm + 1)
|
||||
return;
|
||||
|
||||
sym = find_symbol_from_addr(map, addr, &found_map, &sym_offset);
|
||||
if (sym == NULL)
|
||||
return;
|
||||
|
||||
/* Symbolize the resolved address */
|
||||
len = len - (last_imm - buf);
|
||||
if (sym_offset == 0) {
|
||||
scnprintf(last_imm, len, "%"PRIx64" <%s>",
|
||||
addr, sym->name);
|
||||
} else {
|
||||
scnprintf(last_imm, len, "%"PRIx64" <%s+%#"PRIx64">",
|
||||
addr, sym->name, sym_offset);
|
||||
}
|
||||
map__put(found_map);
|
||||
}
|
||||
}
|
||||
|
||||
static void format_capstone_insn(struct cs_insn *insn, char *buf, size_t len,
|
||||
struct annotate_args *args, u64 addr)
|
||||
{
|
||||
/* TODO: support more architectures */
|
||||
if (arch__is_x86(args->arch))
|
||||
format_capstone_insn_x86(insn, buf, len, args, addr);
|
||||
else if (arch__is_arm64(args->arch))
|
||||
format_capstone_insn_arm64(insn, buf, len, args);
|
||||
else
|
||||
print_default_format(insn, buf, len);
|
||||
}
|
||||
|
||||
struct find_file_offset_data {
|
||||
u64 ip;
|
||||
u64 offset;
|
||||
|
|
@ -446,14 +567,9 @@ int symbol__disassemble_capstone(const char *filename, struct symbol *sym,
|
|||
|
||||
free_count = count = perf_cs_disasm(handle, buf, buf_len, start, buf_len, &insn);
|
||||
for (i = 0, offset = 0; i < count; i++) {
|
||||
int printed;
|
||||
|
||||
printed = scnprintf(disasm_buf, sizeof(disasm_buf),
|
||||
" %-7s %s",
|
||||
insn[i].mnemonic, insn[i].op_str);
|
||||
print_capstone_detail(&insn[i], disasm_buf + printed,
|
||||
sizeof(disasm_buf) - printed, args,
|
||||
start + offset);
|
||||
format_capstone_insn(&insn[i], disasm_buf,
|
||||
sizeof(disasm_buf), args,
|
||||
start + offset);
|
||||
|
||||
args->offset = offset;
|
||||
args->line = disasm_buf;
|
||||
|
|
|
|||
|
|
@ -214,6 +214,11 @@ bool arch__is_powerpc(const struct arch *arch)
|
|||
return arch->id.e_machine == EM_PPC || arch->id.e_machine == EM_PPC64;
|
||||
}
|
||||
|
||||
bool arch__is_arm64(const struct arch *arch)
|
||||
{
|
||||
return arch->id.e_machine == EM_AARCH64;
|
||||
}
|
||||
|
||||
static void ins_ops__delete(struct ins_operands *ops)
|
||||
{
|
||||
if (ops == NULL)
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ struct annotate_args {
|
|||
const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *cpuid);
|
||||
bool arch__is_x86(const struct arch *arch);
|
||||
bool arch__is_powerpc(const struct arch *arch);
|
||||
bool arch__is_arm64(const struct arch *arch);
|
||||
|
||||
extern const struct ins_ops call_ops;
|
||||
extern const struct ins_ops dec_ops;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user