| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644 |
- // SPDX-License-Identifier: GPL-2.0-only
- #include <ctype.h>
- #include <elf.h>
- #ifndef EF_CSKY_ABIMASK
- #define EF_CSKY_ABIMASK 0XF0000000
- #endif
- #include <errno.h>
- #include <fcntl.h>
- #include <inttypes.h>
- #include <libgen.h>
- #include <regex.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <linux/string.h>
- #include <subcmd/run-command.h>
- #include "annotate.h"
- #include "annotate-data.h"
- #include "build-id.h"
- #include "capstone.h"
- #include "debug.h"
- #include "disasm.h"
- #include "dso.h"
- #include "dwarf-regs.h"
- #include "env.h"
- #include "evsel.h"
- #include "libbfd.h"
- #include "llvm.h"
- #include "map.h"
- #include "maps.h"
- #include "namespaces.h"
- #include "srcline.h"
- #include "symbol.h"
- #include "thread.h"
- #include "util.h"
- static regex_t file_lineno;
- /* These can be referred from the arch-dependent code */
- const struct ins_ops call_ops;
- const struct ins_ops dec_ops;
- const struct ins_ops jump_ops;
- const struct ins_ops mov_ops;
- const struct ins_ops nop_ops;
- const struct ins_ops lock_ops;
- const struct ins_ops ret_ops;
- const struct ins_ops load_store_ops;
- const struct ins_ops arithmetic_ops;
- static void ins__sort(struct arch *arch);
- static int disasm_line__parse(char *line, const char **namep, char **rawp);
- static int disasm_line__parse_powerpc(struct disasm_line *dl, struct annotate_args *args);
- static __attribute__((constructor)) void symbol__init_regexpr(void)
- {
- regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED);
- }
- static int arch__grow_instructions(struct arch *arch)
- {
- struct ins *new_instructions;
- size_t new_nr_allocated;
- if (arch->nr_instructions_allocated == 0 && arch->instructions)
- goto grow_from_non_allocated_table;
- new_nr_allocated = arch->nr_instructions_allocated + 128;
- new_instructions = realloc((void *)arch->instructions,
- new_nr_allocated * sizeof(struct ins));
- if (new_instructions == NULL)
- return -1;
- out_update_instructions:
- arch->instructions = new_instructions;
- arch->nr_instructions_allocated = new_nr_allocated;
- return 0;
- grow_from_non_allocated_table:
- new_nr_allocated = arch->nr_instructions + 128;
- new_instructions = calloc(new_nr_allocated, sizeof(struct ins));
- if (new_instructions == NULL)
- return -1;
- memcpy(new_instructions, arch->instructions, arch->nr_instructions * sizeof(struct ins));
- goto out_update_instructions;
- }
- int arch__associate_ins_ops(struct arch *arch, const char *name, const struct ins_ops *ops)
- {
- struct ins *ins;
- if (arch->nr_instructions == arch->nr_instructions_allocated &&
- arch__grow_instructions(arch))
- return -1;
- ins = (struct ins *)&arch->instructions[arch->nr_instructions];
- ins->name = strdup(name);
- if (!ins->name)
- return -1;
- ins->ops = ops;
- arch->nr_instructions++;
- ins__sort(arch);
- return 0;
- }
- static int e_machine_and_eflags__cmp(const struct e_machine_and_e_flags *val1,
- const struct e_machine_and_e_flags *val2)
- {
- if (val1->e_machine == val2->e_machine) {
- if (val1->e_machine != EM_CSKY)
- return 0;
- if ((val1->e_flags & EF_CSKY_ABIMASK) < (val2->e_flags & EF_CSKY_ABIMASK))
- return -1;
- return (val1->e_flags & EF_CSKY_ABIMASK) > (val2->e_flags & EF_CSKY_ABIMASK);
- }
- return val1->e_machine < val2->e_machine ? -1 : 1;
- }
- static int arch__key_cmp(const void *key, const void *archp)
- {
- const struct arch *const *arch = archp;
- return e_machine_and_eflags__cmp(key, &(*arch)->id);
- }
- static int arch__cmp(const void *a, const void *b)
- {
- const struct arch *const *aa = a;
- const struct arch *const *ab = b;
- return e_machine_and_eflags__cmp(&(*aa)->id, &(*ab)->id);
- }
- const struct arch *arch__find(uint16_t e_machine, uint32_t e_flags, const char *cpuid)
- {
- static const struct arch *(*const arch_new_fn[])(const struct e_machine_and_e_flags *id,
- const char *cpuid) = {
- [EM_386] = arch__new_x86,
- [EM_ARC] = arch__new_arc,
- [EM_ARM] = arch__new_arm,
- [EM_AARCH64] = arch__new_arm64,
- [EM_CSKY] = arch__new_csky,
- [EM_LOONGARCH] = arch__new_loongarch,
- [EM_MIPS] = arch__new_mips,
- [EM_PPC64] = arch__new_powerpc,
- [EM_PPC] = arch__new_powerpc,
- [EM_RISCV] = arch__new_riscv64,
- [EM_S390] = arch__new_s390,
- [EM_SPARC] = arch__new_sparc,
- [EM_SPARCV9] = arch__new_sparc,
- [EM_X86_64] = arch__new_x86,
- };
- static const struct arch **archs;
- static size_t num_archs;
- struct e_machine_and_e_flags key = {
- .e_machine = e_machine,
- .e_flags = e_flags,
- };
- const struct arch *result = NULL, **tmp;
- if (num_archs > 0) {
- tmp = bsearch(&key, archs, num_archs, sizeof(*archs), arch__key_cmp);
- if (tmp)
- result = *tmp;
- }
- if (result)
- return result;
- if (e_machine >= ARRAY_SIZE(arch_new_fn) || arch_new_fn[e_machine] == NULL) {
- errno = ENOTSUP;
- return NULL;
- }
- tmp = reallocarray(archs, num_archs + 1, sizeof(*archs));
- if (!tmp)
- return NULL;
- result = arch_new_fn[e_machine](&key, cpuid);
- if (!result) {
- pr_err("%s: failed to initialize %s (%u) arch priv area\n",
- __func__, result->name, e_machine);
- free(tmp);
- return NULL;
- }
- archs = tmp;
- archs[num_archs++] = result;
- qsort(archs, num_archs, sizeof(*archs), arch__cmp);
- return result;
- }
- bool arch__is_x86(const struct arch *arch)
- {
- return arch->id.e_machine == EM_386 || arch->id.e_machine == EM_X86_64;
- }
- bool arch__is_powerpc(const struct arch *arch)
- {
- return arch->id.e_machine == EM_PPC || arch->id.e_machine == EM_PPC64;
- }
- static void ins_ops__delete(struct ins_operands *ops)
- {
- if (ops == NULL)
- return;
- zfree(&ops->source.raw);
- zfree(&ops->source.name);
- zfree(&ops->target.raw);
- zfree(&ops->target.name);
- }
- int ins__raw_scnprintf(const struct ins *ins, char *bf, size_t size,
- struct ins_operands *ops, int max_ins_name)
- {
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->raw);
- }
- int ins__scnprintf(const struct ins *ins, char *bf, size_t size,
- struct ins_operands *ops, int max_ins_name)
- {
- if (ins->ops->scnprintf)
- return ins->ops->scnprintf(ins, bf, size, ops, max_ins_name);
- return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
- }
- bool ins__is_fused(const struct arch *arch, const char *ins1, const char *ins2)
- {
- if (!arch || !arch->ins_is_fused)
- return false;
- return arch->ins_is_fused(arch, ins1, ins2);
- }
- static int call__parse(const struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
- struct disasm_line *dl __maybe_unused)
- {
- char *endptr, *tok, *name;
- struct map *map = ms->map;
- struct addr_map_symbol target;
- ops->target.addr = strtoull(ops->raw, &endptr, 16);
- name = strchr(endptr, '<');
- if (name == NULL)
- goto indirect_call;
- name++;
- if (arch->objdump.skip_functions_char &&
- strchr(name, arch->objdump.skip_functions_char))
- return -1;
- tok = strchr(name, '>');
- if (tok == NULL)
- return -1;
- *tok = '\0';
- ops->target.name = strdup(name);
- *tok = '>';
- if (ops->target.name == NULL)
- return -1;
- find_target:
- target = (struct addr_map_symbol) {
- .ms = { .map = map__get(map), },
- .addr = map__objdump_2mem(map, ops->target.addr),
- };
- if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
- map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
- addr_map_symbol__exit(&target);
- return 0;
- indirect_call:
- tok = strchr(endptr, '*');
- if (tok != NULL) {
- endptr++;
- /* Indirect call can use a non-rip register and offset: callq *0x8(%rbx).
- * Do not parse such instruction. */
- if (strstr(endptr, "(%r") == NULL)
- ops->target.addr = strtoull(endptr, NULL, 16);
- }
- goto find_target;
- }
- int call__scnprintf(const struct ins *ins, char *bf, size_t size,
- struct ins_operands *ops, int max_ins_name)
- {
- if (ops->target.sym)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
- if (ops->target.addr == 0)
- return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
- if (ops->target.name)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.name);
- return scnprintf(bf, size, "%-*s *%" PRIx64, max_ins_name, ins->name, ops->target.addr);
- }
- const struct ins_ops call_ops = {
- .parse = call__parse,
- .scnprintf = call__scnprintf,
- .is_call = true,
- };
- bool ins__is_call(const struct ins *ins)
- {
- return ins->ops && ins->ops->is_call;
- }
- /*
- * Prevents from matching commas in the comment section, e.g.:
- * ffff200008446e70: b.cs ffff2000084470f4 <generic_exec_single+0x314> // b.hs, b.nlast
- *
- * and skip comma as part of function arguments, e.g.:
- * 1d8b4ac <linemap_lookup(line_maps const*, unsigned int)+0xcc>
- */
- static inline const char *validate_comma(const char *c, struct ins_operands *ops)
- {
- if (ops->jump.raw_comment && c > ops->jump.raw_comment)
- return NULL;
- if (ops->jump.raw_func_start && c > ops->jump.raw_func_start)
- return NULL;
- return c;
- }
- static int jump__parse(const struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
- struct disasm_line *dl __maybe_unused)
- {
- struct map *map = ms->map;
- struct symbol *sym = ms->sym;
- struct addr_map_symbol target = {
- .ms = { .map = map__get(map), },
- };
- const char *c = strchr(ops->raw, ',');
- u64 start, end;
- ops->jump.raw_comment = strchr(ops->raw, arch->objdump.comment_char);
- ops->jump.raw_func_start = strchr(ops->raw, '<');
- c = validate_comma(c, ops);
- /*
- * Examples of lines to parse for the _cpp_lex_token@@Base
- * function:
- *
- * 1159e6c: jne 115aa32 <_cpp_lex_token@@Base+0xf92>
- * 1159e8b: jne c469be <cpp_named_operator2name@@Base+0xa72>
- *
- * The first is a jump to an offset inside the same function,
- * the second is to another function, i.e. that 0xa72 is an
- * offset in the cpp_named_operator2name@@base function.
- */
- /*
- * skip over possible up to 2 operands to get to address, e.g.:
- * tbnz w0, #26, ffff0000083cd190 <security_file_permission+0xd0>
- */
- if (c != NULL) {
- c++;
- ops->target.addr = strtoull(c, NULL, 16);
- if (!ops->target.addr) {
- c = strchr(c, ',');
- c = validate_comma(c, ops);
- if (c != NULL) {
- c++;
- ops->target.addr = strtoull(c, NULL, 16);
- }
- }
- } else {
- ops->target.addr = strtoull(ops->raw, NULL, 16);
- }
- target.addr = map__objdump_2mem(map, ops->target.addr);
- start = map__unmap_ip(map, sym->start);
- end = map__unmap_ip(map, sym->end);
- ops->target.outside = target.addr < start || target.addr >= end;
- /*
- * FIXME: things like this in _cpp_lex_token (gcc's cc1 program):
- cpp_named_operator2name@@Base+0xa72
- * Point to a place that is after the cpp_named_operator2name
- * boundaries, i.e. in the ELF symbol table for cc1
- * cpp_named_operator2name is marked as being 32-bytes long, but it in
- * fact is much larger than that, so we seem to need a symbols__find()
- * routine that looks for >= current->start and < next_symbol->start,
- * possibly just for C++ objects?
- *
- * For now lets just make some progress by marking jumps to outside the
- * current function as call like.
- *
- * Actual navigation will come next, with further understanding of how
- * the symbol searching and disassembly should be done.
- */
- if (maps__find_ams(thread__maps(ms->thread), &target) == 0 &&
- map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
- ops->target.sym = target.ms.sym;
- if (!ops->target.outside) {
- ops->target.offset = target.addr - start;
- ops->target.offset_avail = true;
- } else {
- ops->target.offset_avail = false;
- }
- addr_map_symbol__exit(&target);
- return 0;
- }
- int jump__scnprintf(const struct ins *ins, char *bf, size_t size,
- struct ins_operands *ops, int max_ins_name)
- {
- const char *c;
- if (!ops->target.addr || ops->target.offset < 0)
- return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
- if (ops->target.outside && ops->target.sym != NULL)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
- c = strchr(ops->raw, ',');
- c = validate_comma(c, ops);
- if (c != NULL) {
- const char *c2 = strchr(c + 1, ',');
- c2 = validate_comma(c2, ops);
- /* check for 3-op insn */
- if (c2 != NULL)
- c = c2;
- c++;
- /* mirror arch objdump's space-after-comma style */
- if (*c == ' ')
- c++;
- }
- return scnprintf(bf, size, "%-*s %.*s%" PRIx64, max_ins_name,
- ins->name, c ? c - ops->raw : 0, ops->raw,
- ops->target.offset);
- }
- static void jump__delete(struct ins_operands *ops __maybe_unused)
- {
- /*
- * The ops->jump.raw_comment and ops->jump.raw_func_start belong to the
- * raw string, don't free them.
- */
- }
- const struct ins_ops jump_ops = {
- .free = jump__delete,
- .parse = jump__parse,
- .scnprintf = jump__scnprintf,
- .is_jump = true,
- };
- bool ins__is_jump(const struct ins *ins)
- {
- return ins->ops && ins->ops->is_jump;
- }
- static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
- {
- char *endptr, *name, *t;
- if (strstr(raw, "(%rip)") == NULL)
- return 0;
- *addrp = strtoull(comment, &endptr, 16);
- if (endptr == comment)
- return 0;
- name = strchr(endptr, '<');
- if (name == NULL)
- return -1;
- name++;
- t = strchr(name, '>');
- if (t == NULL)
- return 0;
- *t = '\0';
- *namep = strdup(name);
- *t = '>';
- return 0;
- }
- static int lock__parse(const struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
- struct disasm_line *dl __maybe_unused)
- {
- ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
- if (ops->locked.ops == NULL)
- return 0;
- if (disasm_line__parse(ops->raw, &ops->locked.ins.name, &ops->locked.ops->raw) < 0)
- goto out_free_ops;
- ops->locked.ins.ops = ins__find(arch, ops->locked.ins.name, 0);
- if (ops->locked.ins.ops == NULL)
- goto out_free_ops;
- if (ops->locked.ins.ops->parse &&
- ops->locked.ins.ops->parse(arch, ops->locked.ops, ms, NULL) < 0)
- goto out_free_ops;
- return 0;
- out_free_ops:
- zfree(&ops->locked.ops);
- return 0;
- }
- static int lock__scnprintf(const struct ins *ins, char *bf, size_t size,
- struct ins_operands *ops, int max_ins_name)
- {
- int printed;
- if (ops->locked.ins.ops == NULL)
- return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
- printed = scnprintf(bf, size, "%-*s ", max_ins_name, ins->name);
- return printed + ins__scnprintf(&ops->locked.ins, bf + printed,
- size - printed, ops->locked.ops, max_ins_name);
- }
- static void lock__delete(struct ins_operands *ops)
- {
- struct ins *ins = &ops->locked.ins;
- if (ins->ops && ins->ops->free)
- ins->ops->free(ops->locked.ops);
- else
- ins_ops__delete(ops->locked.ops);
- zfree(&ops->locked.ops);
- zfree(&ops->locked.ins.name);
- zfree(&ops->target.raw);
- zfree(&ops->target.name);
- }
- const struct ins_ops lock_ops = {
- .free = lock__delete,
- .parse = lock__parse,
- .scnprintf = lock__scnprintf,
- };
- /*
- * Check if the operand has more than one registers like x86 SIB addressing:
- * 0x1234(%rax, %rbx, 8)
- *
- * But it doesn't care segment selectors like %gs:0x5678(%rcx), so just check
- * the input string after 'memory_ref_char' if exists.
- */
- static bool check_multi_regs(const struct arch *arch, const char *op)
- {
- int count = 0;
- if (arch->objdump.register_char == 0)
- return false;
- if (arch->objdump.memory_ref_char) {
- op = strchr(op, arch->objdump.memory_ref_char);
- if (op == NULL)
- return false;
- }
- while ((op = strchr(op, arch->objdump.register_char)) != NULL) {
- count++;
- op++;
- }
- return count > 1;
- }
- static int mov__parse(const struct arch *arch, struct ins_operands *ops,
- struct map_symbol *ms __maybe_unused,
- struct disasm_line *dl __maybe_unused)
- {
- char *s = strchr(ops->raw, ','), *target, *comment, prev;
- if (s == NULL)
- return -1;
- *s = '\0';
- /*
- * x86 SIB addressing has something like 0x8(%rax, %rcx, 1)
- * then it needs to have the closing parenthesis.
- */
- if (strchr(ops->raw, '(')) {
- *s = ',';
- s = strchr(ops->raw, ')');
- if (s == NULL || s[1] != ',')
- return -1;
- *++s = '\0';
- }
- ops->source.raw = strdup(ops->raw);
- *s = ',';
- if (ops->source.raw == NULL)
- return -1;
- ops->source.multi_regs = check_multi_regs(arch, ops->source.raw);
- target = skip_spaces(++s);
- comment = strchr(s, arch->objdump.comment_char);
- if (comment != NULL)
- s = comment - 1;
- else
- s = strchr(s, '\0') - 1;
- while (s > target && isspace(s[0]))
- --s;
- s++;
- prev = *s;
- *s = '\0';
- ops->target.raw = strdup(target);
- *s = prev;
- if (ops->target.raw == NULL)
- goto out_free_source;
- ops->target.multi_regs = check_multi_regs(arch, ops->target.raw);
- if (comment == NULL)
- return 0;
- comment = skip_spaces(comment);
- comment__symbol(ops->source.raw, comment + 1, &ops->source.addr, &ops->source.name);
- comment__symbol(ops->target.raw, comment + 1, &ops->target.addr, &ops->target.name);
- return 0;
- out_free_source:
- zfree(&ops->source.raw);
- return -1;
- }
- int mov__scnprintf(const struct ins *ins, char *bf, size_t size,
- struct ins_operands *ops, int max_ins_name)
- {
- return scnprintf(bf, size, "%-*s %s,%s", max_ins_name, ins->name,
- ops->source.name ?: ops->source.raw,
- ops->target.name ?: ops->target.raw);
- }
- const struct ins_ops mov_ops = {
- .parse = mov__parse,
- .scnprintf = mov__scnprintf,
- };
- static int dec__parse(const struct arch *arch __maybe_unused, struct ins_operands *ops,
- struct map_symbol *ms __maybe_unused,
- struct disasm_line *dl __maybe_unused)
- {
- char *target, *comment, *s, prev;
- target = s = ops->raw;
- while (s[0] != '\0' && !isspace(s[0]))
- ++s;
- prev = *s;
- *s = '\0';
- ops->target.raw = strdup(target);
- *s = prev;
- if (ops->target.raw == NULL)
- return -1;
- comment = strchr(s, arch->objdump.comment_char);
- if (comment == NULL)
- return 0;
- comment = skip_spaces(comment);
- comment__symbol(ops->target.raw, comment + 1, &ops->target.addr, &ops->target.name);
- return 0;
- }
- static int dec__scnprintf(const struct ins *ins, char *bf, size_t size,
- struct ins_operands *ops, int max_ins_name)
- {
- return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
- ops->target.name ?: ops->target.raw);
- }
- const struct ins_ops dec_ops = {
- .parse = dec__parse,
- .scnprintf = dec__scnprintf,
- };
- static int nop__scnprintf(const struct ins *ins __maybe_unused, char *bf, size_t size,
- struct ins_operands *ops __maybe_unused, int max_ins_name)
- {
- return scnprintf(bf, size, "%-*s", max_ins_name, "nop");
- }
- const struct ins_ops nop_ops = {
- .scnprintf = nop__scnprintf,
- };
- const struct ins_ops ret_ops = {
- .scnprintf = ins__raw_scnprintf,
- };
- static bool ins__is_nop(const struct ins *ins)
- {
- return ins->ops == &nop_ops;
- }
- bool ins__is_ret(const struct ins *ins)
- {
- return ins->ops == &ret_ops;
- }
- bool ins__is_lock(const struct ins *ins)
- {
- return ins->ops == &lock_ops;
- }
- static int ins__key_cmp(const void *name, const void *insp)
- {
- const struct ins *ins = insp;
- return strcmp(name, ins->name);
- }
- static int ins__cmp(const void *a, const void *b)
- {
- const struct ins *ia = a;
- const struct ins *ib = b;
- return strcmp(ia->name, ib->name);
- }
- static void ins__sort(struct arch *arch)
- {
- const int nmemb = arch->nr_instructions;
- qsort((void *)arch->instructions, nmemb, sizeof(struct ins), ins__cmp);
- }
- static const struct ins_ops *__ins__find(const struct arch *arch, const char *name,
- struct disasm_line *dl)
- {
- const struct ins *ins;
- const int nmemb = arch->nr_instructions;
- if (arch__is_powerpc(arch)) {
- /*
- * For powerpc, identify the instruction ops
- * from the opcode using raw_insn.
- */
- const struct ins_ops *ops;
- ops = check_ppc_insn(dl);
- if (ops)
- return ops;
- }
- if (!arch->sorted_instructions) {
- ins__sort((struct arch *)arch);
- ((struct arch *)arch)->sorted_instructions = true;
- }
- ins = bsearch(name, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp);
- if (ins)
- return ins->ops;
- if (arch->insn_suffix) {
- char tmp[32];
- char suffix;
- size_t len = strlen(name);
- if (len == 0 || len >= sizeof(tmp))
- return NULL;
- suffix = name[len - 1];
- if (strchr(arch->insn_suffix, suffix) == NULL)
- return NULL;
- strcpy(tmp, name);
- tmp[len - 1] = '\0'; /* remove the suffix and check again */
- ins = bsearch(tmp, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp);
- }
- return ins ? ins->ops : NULL;
- }
- const struct ins_ops *ins__find(const struct arch *arch, const char *name, struct disasm_line *dl)
- {
- const struct ins_ops *ops = __ins__find(arch, name, dl);
- if (!ops && arch->associate_instruction_ops)
- ops = arch->associate_instruction_ops((struct arch *)arch, name);
- return ops;
- }
- static void disasm_line__init_ins(struct disasm_line *dl, const struct arch *arch,
- struct map_symbol *ms)
- {
- dl->ins.ops = ins__find(arch, dl->ins.name, dl);
- if (!dl->ins.ops)
- return;
- if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, ms, dl) < 0)
- dl->ins.ops = NULL;
- }
- static int disasm_line__parse(char *line, const char **namep, char **rawp)
- {
- char tmp, *name = skip_spaces(line);
- if (name[0] == '\0')
- return -1;
- *rawp = name + 1;
- while ((*rawp)[0] != '\0' && !isspace((*rawp)[0]))
- ++*rawp;
- tmp = (*rawp)[0];
- (*rawp)[0] = '\0';
- *namep = strdup(name);
- if (*namep == NULL)
- goto out;
- (*rawp)[0] = tmp;
- *rawp = strim(*rawp);
- return 0;
- out:
- return -1;
- }
- /*
- * Parses the result captured from symbol__disassemble_*
- * Example, line read from DSO file in powerpc:
- * line: 38 01 81 e8
- * opcode: fetched from arch specific get_opcode_insn
- * rawp_insn: e8810138
- *
- * rawp_insn is used later to extract the reg/offset fields
- */
- #define PPC_OP(op) (((op) >> 26) & 0x3F)
- #define RAW_BYTES 11
- static int disasm_line__parse_powerpc(struct disasm_line *dl, struct annotate_args *args)
- {
- char *line = dl->al.line;
- const char **namep = &dl->ins.name;
- char **rawp = &dl->ops.raw;
- char *tmp_raw_insn, *name_raw_insn = skip_spaces(line);
- char *name = skip_spaces(name_raw_insn + RAW_BYTES);
- int disasm = 0;
- int ret = 0;
- if (args->options->disassembler_used)
- disasm = 1;
- if (name_raw_insn[0] == '\0')
- return -1;
- if (disasm)
- ret = disasm_line__parse(name, namep, rawp);
- else
- *namep = "";
- tmp_raw_insn = strndup(name_raw_insn, 11);
- if (tmp_raw_insn == NULL)
- return -1;
- remove_spaces(tmp_raw_insn);
- sscanf(tmp_raw_insn, "%x", &dl->raw.raw_insn);
- if (disasm)
- dl->raw.raw_insn = be32_to_cpu(dl->raw.raw_insn);
- return ret;
- }
- static void annotation_line__init(struct annotation_line *al,
- struct annotate_args *args,
- int nr)
- {
- al->offset = args->offset;
- al->line = strdup(args->line);
- al->line_nr = args->line_nr;
- al->fileloc = args->fileloc;
- al->data_nr = nr;
- }
- static void annotation_line__exit(struct annotation_line *al)
- {
- zfree_srcline(&al->path);
- zfree(&al->line);
- zfree(&al->cycles);
- zfree(&al->br_cntr);
- }
- static size_t disasm_line_size(int nr)
- {
- struct annotation_line *al;
- return (sizeof(struct disasm_line) + (sizeof(al->data[0]) * nr));
- }
- /*
- * Allocating the disasm annotation line data with
- * following structure:
- *
- * -------------------------------------------
- * struct disasm_line | struct annotation_line
- * -------------------------------------------
- *
- * We have 'struct annotation_line' member as last member
- * of 'struct disasm_line' to have an easy access.
- */
- struct disasm_line *disasm_line__new(struct annotate_args *args)
- {
- struct disasm_line *dl = NULL;
- struct annotation *notes = symbol__annotation(args->ms->sym);
- int nr = notes->src->nr_events;
- dl = zalloc(disasm_line_size(nr));
- if (!dl)
- return NULL;
- annotation_line__init(&dl->al, args, nr);
- if (dl->al.line == NULL)
- goto out_delete;
- if (args->offset != -1) {
- if (arch__is_powerpc(args->arch)) {
- if (disasm_line__parse_powerpc(dl, args) < 0)
- goto out_free_line;
- } else if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0)
- goto out_free_line;
- disasm_line__init_ins(dl, args->arch, args->ms);
- }
- return dl;
- out_free_line:
- zfree(&dl->al.line);
- out_delete:
- free(dl);
- return NULL;
- }
- void disasm_line__free(struct disasm_line *dl)
- {
- if (dl->ins.ops && dl->ins.ops->free)
- dl->ins.ops->free(&dl->ops);
- else
- ins_ops__delete(&dl->ops);
- zfree(&dl->ins.name);
- annotation_line__exit(&dl->al);
- free(dl);
- }
- int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw, int max_ins_name)
- {
- if (raw || !dl->ins.ops)
- return scnprintf(bf, size, "%-*s %s", max_ins_name, dl->ins.name, dl->ops.raw);
- return ins__scnprintf(&dl->ins, bf, size, &dl->ops, max_ins_name);
- }
- /*
- * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw)
- * which looks like following
- *
- * 0000000000415500 <_init>:
- * 415500: sub $0x8,%rsp
- * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8>
- * 41550b: test %rax,%rax
- * 41550e: je 415515 <_init+0x15>
- * 415510: callq 416e70 <__gmon_start__@plt>
- * 415515: add $0x8,%rsp
- * 415519: retq
- *
- * it will be parsed and saved into struct disasm_line as
- * <offset> <name> <ops.raw>
- *
- * The offset will be a relative offset from the start of the symbol and -1
- * means that it's not a disassembly line so should be treated differently.
- * The ops.raw part will be parsed further according to type of the instruction.
- */
- static int symbol__parse_objdump_line(struct symbol *sym,
- struct annotate_args *args,
- char *parsed_line, int *line_nr, char **fileloc)
- {
- struct map *map = args->ms->map;
- struct annotation *notes = symbol__annotation(sym);
- struct disasm_line *dl;
- char *tmp;
- s64 line_ip, offset = -1;
- regmatch_t match[2];
- /* /filename:linenr ? Save line number and ignore. */
- if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) {
- *line_nr = atoi(parsed_line + match[1].rm_so);
- free(*fileloc);
- *fileloc = strdup(parsed_line);
- return 0;
- }
- /* Process hex address followed by ':'. */
- line_ip = strtoull(parsed_line, &tmp, 16);
- if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') {
- u64 start = map__rip_2objdump(map, sym->start),
- end = map__rip_2objdump(map, sym->end);
- offset = line_ip - start;
- if ((u64)line_ip < start || (u64)line_ip >= end)
- offset = -1;
- else
- parsed_line = tmp + 1;
- }
- args->offset = offset;
- args->line = parsed_line;
- args->line_nr = *line_nr;
- args->fileloc = *fileloc;
- args->ms->sym = sym;
- dl = disasm_line__new(args);
- (*line_nr)++;
- if (dl == NULL)
- return -1;
- if (!disasm_line__has_local_offset(dl)) {
- dl->ops.target.offset = dl->ops.target.addr -
- map__rip_2objdump(map, sym->start);
- dl->ops.target.offset_avail = true;
- }
- /* kcore has no symbols, so add the call target symbol */
- if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.sym) {
- struct addr_map_symbol target = {
- .addr = dl->ops.target.addr,
- .ms = { .map = map__get(map), },
- };
- if (!maps__find_ams(thread__maps(args->ms->thread), &target) &&
- target.ms.sym->start == target.al_addr)
- dl->ops.target.sym = target.ms.sym;
- addr_map_symbol__exit(&target);
- }
- annotation_line__add(&dl->al, ¬es->src->source);
- return 0;
- }
- static void delete_last_nop(struct symbol *sym)
- {
- struct annotation *notes = symbol__annotation(sym);
- struct list_head *list = ¬es->src->source;
- struct disasm_line *dl;
- while (!list_empty(list)) {
- dl = list_entry(list->prev, struct disasm_line, al.node);
- if (dl->ins.ops) {
- if (!ins__is_nop(&dl->ins))
- return;
- } else {
- if (!strstr(dl->al.line, " nop ") &&
- !strstr(dl->al.line, " nopl ") &&
- !strstr(dl->al.line, " nopw "))
- return;
- }
- list_del_init(&dl->al.node);
- disasm_line__free(dl);
- }
- }
- int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, size_t buflen)
- {
- struct dso *dso = map__dso(ms->map);
- BUG_ON(buflen == 0);
- if (errnum >= 0) {
- str_error_r(errnum, buf, buflen);
- return 0;
- }
- switch (errnum) {
- case SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX: {
- char bf[SBUILD_ID_SIZE + 15] = " with build id ";
- char *build_id_msg = NULL;
- if (dso__has_build_id(dso)) {
- build_id__snprintf(dso__bid(dso), bf + 15, sizeof(bf) - 15);
- build_id_msg = bf;
- }
- scnprintf(buf, buflen,
- "No vmlinux file%s\nwas found in the path.\n\n"
- "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n"
- "Please use:\n\n"
- " perf buildid-cache -vu vmlinux\n\n"
- "or:\n\n"
- " --vmlinux vmlinux\n", build_id_msg ?: "");
- }
- break;
- case SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF:
- scnprintf(buf, buflen, "Please link with binutils's libopcode to enable BPF annotation");
- break;
- case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP:
- scnprintf(buf, buflen, "Problems with arch specific instruction name regular expressions.");
- break;
- case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING:
- scnprintf(buf, buflen, "Problems while parsing the CPUID in the arch specific initialization.");
- break;
- case SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE:
- scnprintf(buf, buflen, "Invalid BPF file: %s.", dso__long_name(dso));
- break;
- case SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF:
- scnprintf(buf, buflen, "The %s BPF file has no BTF section, compile with -g or use pahole -J.",
- dso__long_name(dso));
- break;
- case SYMBOL_ANNOTATE_ERRNO__COULDNT_DETERMINE_FILE_TYPE:
- scnprintf(buf, buflen, "Couldn't determine the file %s type.", dso__long_name(dso));
- break;
- default:
- scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum);
- break;
- }
- return 0;
- }
- static int dso__disassemble_filename(struct dso *dso, char *filename, size_t filename_size)
- {
- char linkname[PATH_MAX];
- char *build_id_filename;
- char *build_id_path = NULL;
- char *pos;
- int len;
- if (dso__symtab_type(dso) == DSO_BINARY_TYPE__KALLSYMS &&
- !dso__is_kcore(dso))
- return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX;
- build_id_filename = dso__build_id_filename(dso, NULL, 0, false);
- if (build_id_filename) {
- __symbol__join_symfs(filename, filename_size, build_id_filename);
- free(build_id_filename);
- } else {
- if (dso__has_build_id(dso))
- return ENOMEM;
- goto fallback;
- }
- build_id_path = strdup(filename);
- if (!build_id_path)
- return ENOMEM;
- /*
- * old style build-id cache has name of XX/XXXXXXX.. while
- * new style has XX/XXXXXXX../{elf,kallsyms,vdso}.
- * extract the build-id part of dirname in the new style only.
- */
- pos = strrchr(build_id_path, '/');
- if (pos && strlen(pos) < SBUILD_ID_SIZE - 2)
- dirname(build_id_path);
- if (dso__is_kcore(dso))
- goto fallback;
- len = readlink(build_id_path, linkname, sizeof(linkname) - 1);
- if (len < 0)
- goto fallback;
- linkname[len] = '\0';
- if (strstr(linkname, DSO__NAME_KALLSYMS) ||
- access(filename, R_OK)) {
- fallback:
- /*
- * If we don't have build-ids or the build-id file isn't in the
- * cache, or is just a kallsyms file, well, lets hope that this
- * DSO is the same as when 'perf record' ran.
- */
- if (dso__kernel(dso) && dso__long_name(dso)[0] == '/')
- snprintf(filename, filename_size, "%s", dso__long_name(dso));
- else
- __symbol__join_symfs(filename, filename_size, dso__long_name(dso));
- mutex_lock(dso__lock(dso));
- if (access(filename, R_OK) && errno == ENOENT && dso__nsinfo(dso)) {
- char *new_name = dso__filename_with_chroot(dso, filename);
- if (new_name) {
- strlcpy(filename, new_name, filename_size);
- free(new_name);
- }
- }
- mutex_unlock(dso__lock(dso));
- } else if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
- dso__set_binary_type(dso, DSO_BINARY_TYPE__BUILD_ID_CACHE);
- }
- free(build_id_path);
- return 0;
- }
- static int symbol__disassemble_raw(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);
- u64 start = map__rip_2objdump(map, sym->start);
- u64 end = map__rip_2objdump(map, sym->end);
- u64 len = end - start;
- u64 offset;
- int i, count;
- u8 *buf = NULL;
- char disasm_buf[512];
- struct disasm_line *dl;
- u32 *line;
- /* Return if objdump is specified explicitly */
- if (args->options->objdump_path)
- return -1;
- pr_debug("Reading raw instruction from : %s using dso__data_read_offset\n", filename);
- buf = malloc(len);
- if (buf == NULL)
- goto err;
- count = dso__data_read_offset(dso, NULL, sym->start, buf, len);
- line = (u32 *)buf;
- 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);
- /* Each raw instruction is 4 byte */
- count = len/4;
- for (i = 0, offset = 0; i < count; i++) {
- args->offset = offset;
- sprintf(args->line, "%x", line[i]);
- dl = disasm_line__new(args);
- if (dl == NULL)
- break;
- annotation_line__add(&dl->al, ¬es->src->source);
- offset += 4;
- }
- /* It failed in the middle */
- if (offset != len) {
- struct list_head *list = ¬es->src->source;
- /* Discard all lines and fallback to objdump */
- while (!list_empty(list)) {
- dl = list_first_entry(list, struct disasm_line, al.node);
- list_del_init(&dl->al.node);
- disasm_line__free(dl);
- }
- count = -1;
- }
- out:
- free(buf);
- return count < 0 ? count : 0;
- err:
- count = -1;
- goto out;
- }
- /*
- * 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
- * allocation fails then NULL is returned.
- */
- char *expand_tabs(char *line, char **storage, size_t *storage_len)
- {
- size_t i, src, dst, len, new_storage_len, num_tabs;
- char *new_line;
- size_t line_len = strlen(line);
- for (num_tabs = 0, i = 0; i < line_len; i++)
- if (line[i] == '\t')
- num_tabs++;
- if (num_tabs == 0)
- return line;
- /*
- * Space for the line and '\0', less the leading and trailing
- * spaces. Each tab may introduce 7 additional spaces.
- */
- new_storage_len = line_len + 1 + (num_tabs * 7);
- new_line = malloc(new_storage_len);
- if (new_line == NULL) {
- pr_err("Failure allocating memory for tab expansion\n");
- return NULL;
- }
- /*
- * Copy regions starting at src and expand tabs. If there are two
- * adjacent tabs then 'src == i', the memcpy is of size 0 and the spaces
- * are inserted.
- */
- for (i = 0, src = 0, dst = 0; i < line_len && num_tabs; i++) {
- if (line[i] == '\t') {
- len = i - src;
- memcpy(&new_line[dst], &line[src], len);
- dst += len;
- new_line[dst++] = ' ';
- while (dst % 8 != 0)
- new_line[dst++] = ' ';
- src = i + 1;
- num_tabs--;
- }
- }
- /* Expand the last region. */
- len = line_len - src;
- memcpy(&new_line[dst], &line[src], len);
- dst += len;
- new_line[dst] = '\0';
- free(*storage);
- *storage = new_line;
- *storage_len = new_storage_len;
- return new_line;
- }
- static int symbol__disassemble_bpf_image(struct symbol *sym, struct annotate_args *args)
- {
- struct annotation *notes = symbol__annotation(sym);
- struct disasm_line *dl;
- args->offset = -1;
- args->line = strdup("to be implemented");
- args->line_nr = 0;
- args->fileloc = NULL;
- dl = disasm_line__new(args);
- if (dl)
- annotation_line__add(&dl->al, ¬es->src->source);
- zfree(&args->line);
- return 0;
- }
- static int symbol__disassemble_objdump(const char *filename, struct symbol *sym,
- struct annotate_args *args)
- {
- struct annotation_options *opts = &annotate_opts;
- struct map *map = args->ms->map;
- struct dso *dso = map__dso(map);
- char *command;
- FILE *file;
- int lineno = 0;
- char *fileloc = NULL;
- int nline;
- char *line;
- size_t line_len;
- const char *objdump_argv[] = {
- "/bin/sh",
- "-c",
- NULL, /* Will be the objdump command to run. */
- "--",
- NULL, /* Will be the symfs path. */
- NULL,
- };
- struct child_process objdump_process;
- int err;
- if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_PROG_INFO)
- return symbol__disassemble_bpf_libbfd(sym, args);
- if (dso__binary_type(dso) == DSO_BINARY_TYPE__BPF_IMAGE)
- return symbol__disassemble_bpf_image(sym, args);
- err = asprintf(&command,
- "%s %s%s --start-address=0x%016" PRIx64
- " --stop-address=0x%016" PRIx64
- " %s -d %s %s %s %c%s%c %s%s -C \"$1\"",
- opts->objdump_path ?: "objdump",
- opts->disassembler_style ? "-M " : "",
- opts->disassembler_style ?: "",
- map__rip_2objdump(map, sym->start),
- map__rip_2objdump(map, sym->end),
- opts->show_linenr ? "-l" : "",
- opts->show_asm_raw ? "" : "--no-show-raw-insn",
- opts->annotate_src ? "-S" : "",
- opts->prefix ? "--prefix " : "",
- opts->prefix ? '"' : ' ',
- opts->prefix ?: "",
- opts->prefix ? '"' : ' ',
- opts->prefix_strip ? "--prefix-strip=" : "",
- opts->prefix_strip ?: "");
- if (err < 0) {
- pr_err("Failure allocating memory for the command to run\n");
- return err;
- }
- pr_debug("Executing: %s\n", command);
- objdump_argv[2] = command;
- objdump_argv[4] = filename;
- /* Create a pipe to read from for stdout */
- memset(&objdump_process, 0, sizeof(objdump_process));
- objdump_process.argv = objdump_argv;
- objdump_process.out = -1;
- objdump_process.err = -1;
- objdump_process.no_stderr = 1;
- if (start_command(&objdump_process)) {
- pr_err("Failure starting to run %s\n", command);
- err = -1;
- goto out_free_command;
- }
- file = fdopen(objdump_process.out, "r");
- if (!file) {
- pr_err("Failure creating FILE stream for %s\n", command);
- /*
- * If we were using debug info should retry with
- * original binary.
- */
- err = -1;
- goto out_close_stdout;
- }
- /* Storage for getline. */
- line = NULL;
- line_len = 0;
- nline = 0;
- while (!feof(file)) {
- const char *match;
- char *expanded_line;
- if (getline(&line, &line_len, file) < 0 || !line)
- break;
- /* Skip lines containing "filename:" */
- match = strstr(line, filename);
- if (match && match[strlen(filename)] == ':')
- continue;
- expanded_line = strim(line);
- expanded_line = expand_tabs(expanded_line, &line, &line_len);
- if (!expanded_line)
- break;
- /*
- * The source code line number (lineno) needs to be kept in
- * across calls to symbol__parse_objdump_line(), so that it
- * can associate it with the instructions till the next one.
- * See disasm_line__new() and struct disasm_line::line_nr.
- */
- if (symbol__parse_objdump_line(sym, args, expanded_line,
- &lineno, &fileloc) < 0)
- break;
- nline++;
- }
- free(line);
- free(fileloc);
- err = finish_command(&objdump_process);
- if (err)
- pr_err("Error running %s\n", command);
- if (nline == 0) {
- err = -1;
- pr_err("No output from %s\n", command);
- }
- /*
- * kallsyms does not have symbol sizes so there may a nop at the end.
- * Remove it.
- */
- if (dso__is_kcore(dso))
- delete_last_nop(sym);
- fclose(file);
- out_close_stdout:
- close(objdump_process.out);
- out_free_command:
- free(command);
- return err;
- }
- int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
- {
- struct annotation_options *options = args->options;
- struct map *map = args->ms->map;
- struct dso *dso = map__dso(map);
- char symfs_filename[PATH_MAX];
- bool delete_extract = false;
- struct kcore_extract kce;
- bool decomp = false;
- int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename));
- if (err)
- return err;
- pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
- symfs_filename, sym->name, map__unmap_ip(map, sym->start),
- map__unmap_ip(map, sym->end));
- pr_debug("annotating [%p] %30s : [%p] %30s\n", dso, dso__long_name(dso), sym, sym->name);
- if (dso__binary_type(dso) == DSO_BINARY_TYPE__NOT_FOUND) {
- return SYMBOL_ANNOTATE_ERRNO__COULDNT_DETERMINE_FILE_TYPE;
- } else if (dso__is_kcore(dso)) {
- kce.addr = map__rip_2objdump(map, sym->start);
- kce.kcore_filename = symfs_filename;
- kce.len = sym->end - sym->start;
- kce.offs = sym->start;
- if (!kcore_extract__create(&kce)) {
- delete_extract = true;
- strlcpy(symfs_filename, kce.extract_filename, sizeof(symfs_filename));
- }
- } else if (dso__needs_decompress(dso)) {
- char tmp[KMOD_DECOMP_LEN];
- if (dso__decompress_kmodule_path(dso, symfs_filename, tmp, sizeof(tmp)) < 0)
- return -1;
- decomp = true;
- strcpy(symfs_filename, tmp);
- }
- /*
- * For powerpc data type profiling, use the dso__data_read_offset to
- * read raw instruction directly and interpret the binary code to
- * understand instructions and register fields. For sort keys as type
- * and typeoff, disassemble to mnemonic notation is not required in
- * case of powerpc.
- */
- if (arch__is_powerpc(args->arch)) {
- extern const char *sort_order;
- if (sort_order && !strstr(sort_order, "sym")) {
- err = symbol__disassemble_raw(symfs_filename, sym, args);
- if (err == 0)
- goto out_remove_tmp;
- err = symbol__disassemble_capstone_powerpc(symfs_filename, sym, args);
- if (err == 0)
- goto out_remove_tmp;
- }
- }
- /* FIXME: LLVM and CAPSTONE should support source code */
- if (options->annotate_src && !options->hide_src_code) {
- err = symbol__disassemble_objdump(symfs_filename, sym, args);
- if (err == 0)
- goto out_remove_tmp;
- }
- err = -1;
- for (u8 i = 0; i < ARRAY_SIZE(options->disassemblers) && err != 0; i++) {
- enum perf_disassembler dis = options->disassemblers[i];
- switch (dis) {
- case PERF_DISASM_LLVM:
- args->options->disassembler_used = PERF_DISASM_LLVM;
- err = symbol__disassemble_llvm(symfs_filename, sym, args);
- break;
- case PERF_DISASM_CAPSTONE:
- args->options->disassembler_used = PERF_DISASM_CAPSTONE;
- err = symbol__disassemble_capstone(symfs_filename, sym, args);
- break;
- case PERF_DISASM_OBJDUMP:
- args->options->disassembler_used = PERF_DISASM_OBJDUMP;
- err = symbol__disassemble_objdump(symfs_filename, sym, args);
- break;
- case PERF_DISASM_UNKNOWN: /* End of disassemblers. */
- default:
- args->options->disassembler_used = PERF_DISASM_UNKNOWN;
- goto out_remove_tmp;
- }
- if (err == 0)
- pr_debug("Disassembled with %s\n", perf_disassembler__strs[dis]);
- }
- out_remove_tmp:
- if (decomp)
- unlink(symfs_filename);
- if (delete_extract)
- kcore_extract__delete(&kce);
- return err;
- }
|