// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> */ #define _GNU_SOURCE#include <fnmatch.h> #include <objtool/arch.h>#include <objtool/check.h>#include <objtool/disas.h>#include <objtool/special.h>#include <objtool/warn.h> #include <bfd.h>#include <linux/string.h>#include <tools/dis-asm-compat.h> /* * Size of the buffer for storing the result of disassembling * a single instruction. */#define DISAS_RESULT_SIZE 1024 struct disas_context { struct objtool_file *file; struct instruction *insn; bool alt_applied; char result[DISAS_RESULT_SIZE]; disassembler_ftype disassembler; struct disassemble_info info;}; /* * Maximum number of alternatives */#define DISAS_ALT_MAX 5 /* * Maximum number of instructions per alternative */#define DISAS_ALT_INSN_MAX 50 /* * Information to disassemble an alternative */struct disas_alt { struct instruction *orig_insn; /* original instruction */ struct alternative *alt; /* alternative or NULL if default code */ char *name; /* name for this alternative */ int width; /* formatting width */ struct { char *str; /* instruction string */ int offset; /* instruction offset */ int nops; /* number of nops */ } insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */ int insn_idx; /* index of the next instruction to print */}; #define DALT_DEFAULT(dalt) (!(dalt)->alt)#define DALT_INSN(dalt) (DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn)#define DALT_GROUP(dalt) (DALT_INSN(dalt)->alt_group)#define DALT_ALTID(dalt) ((dalt)->orig_insn->offset) #define ALT_FLAGS_SHIFT 16#define ALT_FLAG_NOT (1 << 0)#define ALT_FLAG_DIRECT_CALL (1 << 1)#define ALT_FEATURE_MASK ((1 << ALT_FLAGS_SHIFT) - 1) static int alt_feature(unsigned int ft_flags){ return (ft_flags & ALT_FEATURE_MASK);} static int alt_flags(unsigned int ft_flags){ return (ft_flags >> ALT_FLAGS_SHIFT);} /* * Wrapper around asprintf() to allocate and format a string. * Return the allocated string or NULL on error. */static char *strfmt(const char *fmt, ...){ va_list ap; char *str; int rv; va_start(ap, fmt); rv = vasprintf(&str, fmt, ap); va_end(ap); return rv == -1 ? NULL : str;} static int sprint_name(char *str, const char *name, unsigned long offset){ int len; if (offset) len = sprintf(str, "%s+0x%lx", name, offset); else len = sprintf(str, "%s", name); return len;} #define DINFO_FPRINTF(dinfo, ...) \ ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))#define bfd_vma_fmt \ __builtin_choose_expr(sizeof(bfd_vma) == sizeof(unsigned long), "%#lx <%s>", "%#llx <%s>") static int disas_result_fprintf(struct disas_context *dctx, const char *fmt, va_list ap){ char *buf = dctx->result; int avail, len; len = strlen(buf); if (len >= DISAS_RESULT_SIZE - 1) { WARN_FUNC(dctx->insn->sec, dctx->insn->offset, "disassembly buffer is full"); return -1; } avail = DISAS_RESULT_SIZE - len; len = vsnprintf(buf + len, avail, fmt, ap); if (len < 0 || len >= avail) { WARN_FUNC(dctx->insn->sec, dctx->insn->offset, "disassembly buffer is truncated"); return -1; } return 0;} static int disas_fprintf(void *stream, const char *fmt, ...){ va_list arg; int rv; va_start(arg, fmt); rv = disas_result_fprintf(stream, fmt, arg); va_end(arg); return rv;} /* * For init_disassemble_info_compat(). */static int disas_fprintf_styled(void *stream, enum disassembler_style style, const char *fmt, ...){ va_list arg; int rv; va_start(arg, fmt); rv = disas_result_fprintf(stream, fmt, arg); va_end(arg); return rv;} static void disas_print_addr_sym(struct section *sec, struct symbol *sym, bfd_vma addr, struct disassemble_info *dinfo){ char symstr[1024]; char *str; if (sym) { sprint_name(symstr, sym->name, addr - sym->offset); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, symstr); } else { str = offstr(sec, addr); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str); free(str); }} static bool disas_print_addr_alt(bfd_vma addr, struct disassemble_info *dinfo){ struct disas_context *dctx = dinfo->application_data; struct instruction *orig_first_insn; struct alt_group *alt_group; unsigned long offset; struct symbol *sym; /* * Check if we are processing an alternative at the original * instruction address (i.e. if alt_applied is true) and if * we are referencing an address inside the alternative. * * For example, this happens if there is a branch inside an * alternative. In that case, the address should be updated * to a reference inside the original instruction flow. */ if (!dctx->alt_applied) return false; alt_group = dctx->insn->alt_group; if (!alt_group || !alt_group->orig_group || addr < alt_group->first_insn->offset || addr > alt_group->last_insn->offset) return false; orig_first_insn = alt_group->orig_group->first_insn; offset = addr - alt_group->first_insn->offset; addr = orig_first_insn->offset + offset; sym = insn_sym(orig_first_insn); disas_print_addr_sym(orig_first_insn->sec, sym, addr, dinfo); return true;} static void disas_print_addr_noreloc(bfd_vma addr, struct disassemble_info *dinfo){ struct disas_context *dctx = dinfo->application_data; struct instruction *insn = dctx->insn; struct symbol *sym = insn_sym(insn); if (disas_print_addr_alt(addr, dinfo)) return; if (sym && (addr < sym->offset || addr >= sym->offset + sym->len)) sym = NULL; disas_print_addr_sym(insn->sec, sym, addr, dinfo);} static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo){ struct disas_context *dctx = dinfo->application_data; struct instruction *insn = dctx->insn; unsigned long offset; struct reloc *reloc; char symstr[1024]; char *str; reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec, insn->offset, insn->len); if (!reloc) { /* * There is no relocation for this instruction although * the address to resolve points to the next instruction. * So this is an effective reference to the next IP, for * example: "lea 0x0(%rip),%rdi". The kernel can reference * the next IP with _THIS_IP_ macro. */ DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, "_THIS_IP_"); return; } offset = arch_insn_adjusted_addend(insn, reloc); /* * If the relocation symbol is a section name (for example ".bss") * then we try to further resolve the name. */ if (is_sec_sym(reloc->sym)) { str = offstr(reloc->sym->sec, reloc->sym->offset + offset); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str); free(str); } else { sprint_name(symstr, reloc->sym->name, offset); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, symstr); }} /* * Resolve an address into a "<symbol>+<offset>" string. */static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo){ struct disas_context *dctx = dinfo->application_data; struct instruction *insn = dctx->insn; struct instruction *jump_dest; struct symbol *sym; bool is_reloc; /* * If the instruction is a call/jump and it references a * destination then this is likely the address we are looking * up. So check it first. */ jump_dest = insn->jump_dest; if (jump_dest && insn_sym(jump_dest) && jump_dest->offset == addr) { if (!disas_print_addr_alt(addr, dinfo)) disas_print_addr_sym(jump_dest->sec, insn_sym(jump_dest), addr, dinfo); return; } /* * If the address points to the next instruction then there is * probably a relocation. It can be a false positive when the * current instruction is referencing the address of the next * instruction. This particular case will be handled in * disas_print_addr_reloc(). */ is_reloc = (addr == insn->offset + insn->len); /* * The call destination offset can be the address we are looking * up, or 0 if there is a relocation. */ sym = insn_call_dest(insn); if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) { DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, sym->name); return; } if (!is_reloc) disas_print_addr_noreloc(addr, dinfo); else disas_print_addr_reloc(addr, dinfo);} /* * Initialize disassemble info arch, mach (32 or 64-bit) and options. */int disas_info_init(struct disassemble_info *dinfo, int arch, int mach32, int mach64, const char *options){ struct disas_context *dctx = dinfo->application_data; struct objtool_file *file = dctx->file; dinfo->arch = arch; switch (file->elf->ehdr.e_ident[EI_CLASS]) { case ELFCLASS32: dinfo->mach = mach32; break; case ELFCLASS64: dinfo->mach = mach64; break; default: return -1; } dinfo->disassembler_options = options; return 0;} struct disas_context *disas_context_create(struct objtool_file *file){ struct disas_context *dctx; struct disassemble_info *dinfo; int err; dctx = malloc(sizeof(*dctx)); if (!dctx) { WARN("failed to allocate disassembly context"); return NULL; } dctx->file = file; dinfo = &dctx->info; init_disassemble_info_compat(dinfo, dctx, disas_fprintf, disas_fprintf_styled); dinfo->read_memory_func = buffer_read_memory; dinfo->print_address_func = disas_print_address; dinfo->application_data = dctx; /* * bfd_openr() is not used to avoid doing ELF data processing * and caching that has already being done. Here, we just need * to identify the target file so we call an arch specific * function to fill some disassemble info (arch, mach). */ dinfo->arch = bfd_arch_unknown; dinfo->mach = 0; err = arch_disas_info_init(dinfo); if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { WARN("failed to init disassembly arch"); goto error; } dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; disassemble_init_for_target(dinfo); dctx->disassembler = disassembler(dinfo->arch, dinfo->endian == BFD_ENDIAN_BIG, dinfo->mach, NULL); if (!dctx->disassembler) { WARN("failed to create disassembler function"); goto error; } return dctx; error: free(dctx); return NULL;} void disas_context_destroy(struct disas_context *dctx){ free(dctx);} char *disas_result(struct disas_context *dctx){ return dctx->result;} #define DISAS_INSN_OFFSET_SPACE 10#define DISAS_INSN_SPACE 60 #define DISAS_PRINSN(dctx, insn, depth) \ disas_print_insn(stdout, dctx, insn, depth, "\n") /* * Print a message in the instruction flow. If sec is not NULL then the * address at the section offset is printed in addition of the message, * otherwise only the message is printed. */static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset, int depth, const char *format, va_list ap){ const char *addr_str; int i, n; int len; len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE; if (depth < 0) { len += depth; depth = 0; } n = 0; if (sec) { addr_str = offstr(sec, offset); n += fprintf(stream, "%6lx: %-*s ", offset, len, addr_str); free((char *)addr_str); } else { len += DISAS_INSN_OFFSET_SPACE + 1; n += fprintf(stream, "%-*s", len, ""); } /* print vertical bars to show the code flow */ for (i = 0; i < depth; i++) n += fprintf(stream, "| "); if (format) n += vfprintf(stream, format, ap); return n;} static int disas_print(FILE *stream, struct section *sec, unsigned long offset, int depth, const char *format, ...){ va_list args; int len; va_start(args, format); len = disas_vprint(stream, sec, offset, depth, format, args); va_end(args); return len;} /* * Print a message in the instruction flow. If insn is not NULL then * the instruction address is printed in addition of the message, * otherwise only the message is printed. In all cases, the instruction * itself is not printed. */void disas_print_info(FILE *stream, struct instruction *insn, int depth, const char *format, ...){ struct section *sec; unsigned long off; va_list args; if (insn) { sec = insn->sec; off = insn->offset; } else { sec = NULL; off = 0; } va_start(args, format); disas_vprint(stream, sec, off, depth, format, args);Lines 1–500 of 1248. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> */ #define _GNU_SOURCE #include <fnmatch.h> #include <objtool/arch.h> #include <objtool/check.h> #include <objtool/disas.h> #include <objtool/special.h> #include <objtool/warn.h> #include <bfd.h> #include <linux/string.h> #include <tools/dis-asm-compat.h> /* * Size of the buffer for storing the result of disassembling * a single instruction. */ #define DISAS_RESULT_SIZE 1024 struct disas_context { struct objtool_file *file; struct instruction *insn; bool alt_applied; char result[DISAS_RESULT_SIZE]; disassembler_ftype disassembler; struct disassemble_info info; }; /* * Maximum number of alternatives */ #define DISAS_ALT_MAX 5 /* * Maximum number of instructions per alternative */ #define DISAS_ALT_INSN_MAX 50 /* * Information to disassemble an alternative */ struct disas_alt { struct instruction *orig_insn; /* original instruction */ struct alternative *alt; /* alternative or NULL if default code */ char *name; /* name for this alternative */ int width; /* formatting width */ struct { char *str; /* instruction string */ int offset; /* instruction offset */ int nops; /* number of nops */ } insn[DISAS_ALT_INSN_MAX]; /* alternative instructions */ int insn_idx; /* index of the next instruction to print */ }; #define DALT_DEFAULT(dalt) (!(dalt)->alt) #define DALT_INSN(dalt) (DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn) #define DALT_GROUP(dalt) (DALT_INSN(dalt)->alt_group) #define DALT_ALTID(dalt) ((dalt)->orig_insn->offset) #define ALT_FLAGS_SHIFT 16 #define ALT_FLAG_NOT (1 << 0) #define ALT_FLAG_DIRECT_CALL (1 << 1) #define ALT_FEATURE_MASK ((1 << ALT_FLAGS_SHIFT) - 1) static int alt_feature(unsigned int ft_flags) { return (ft_flags & ALT_FEATURE_MASK); } static int alt_flags(unsigned int ft_flags) { return (ft_flags >> ALT_FLAGS_SHIFT); } /* * Wrapper around asprintf() to allocate and format a string. * Return the allocated string or NULL on error. */ static char *strfmt(const char *fmt, ...) { va_list ap; char *str; int rv; va_start(ap, fmt); rv = vasprintf(&str, fmt, ap); va_end(ap); return rv == -1 ? NULL : str; } static int sprint_name(char *str, const char *name, unsigned long offset) { int len; if (offset) len = sprintf(str, "%s+0x%lx", name, offset); else len = sprintf(str, "%s", name); return len; } #define DINFO_FPRINTF(dinfo, ...) \ ((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__)) #define bfd_vma_fmt \ __builtin_choose_expr(sizeof(bfd_vma) == sizeof(unsigned long), "%#lx <%s>", "%#llx <%s>") static int disas_result_fprintf(struct disas_context *dctx, const char *fmt, va_list ap) { char *buf = dctx->result; int avail, len; len = strlen(buf); if (len >= DISAS_RESULT_SIZE - 1) { WARN_FUNC(dctx->insn->sec, dctx->insn->offset, "disassembly buffer is full"); return -1; } avail = DISAS_RESULT_SIZE - len; len = vsnprintf(buf + len, avail, fmt, ap); if (len < 0 || len >= avail) { WARN_FUNC(dctx->insn->sec, dctx->insn->offset, "disassembly buffer is truncated"); return -1; } return 0; } static int disas_fprintf(void *stream, const char *fmt, ...) { va_list arg; int rv; va_start(arg, fmt); rv = disas_result_fprintf(stream, fmt, arg); va_end(arg); return rv; } /* * For init_disassemble_info_compat(). */ static int disas_fprintf_styled(void *stream, enum disassembler_style style, const char *fmt, ...) { va_list arg; int rv; va_start(arg, fmt); rv = disas_result_fprintf(stream, fmt, arg); va_end(arg); return rv; } static void disas_print_addr_sym(struct section *sec, struct symbol *sym, bfd_vma addr, struct disassemble_info *dinfo) { char symstr[1024]; char *str; if (sym) { sprint_name(symstr, sym->name, addr - sym->offset); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, symstr); } else { str = offstr(sec, addr); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str); free(str); } } static bool disas_print_addr_alt(bfd_vma addr, struct disassemble_info *dinfo) { struct disas_context *dctx = dinfo->application_data; struct instruction *orig_first_insn; struct alt_group *alt_group; unsigned long offset; struct symbol *sym; /* * Check if we are processing an alternative at the original * instruction address (i.e. if alt_applied is true) and if * we are referencing an address inside the alternative. * * For example, this happens if there is a branch inside an * alternative. In that case, the address should be updated * to a reference inside the original instruction flow. */ if (!dctx->alt_applied) return false; alt_group = dctx->insn->alt_group; if (!alt_group || !alt_group->orig_group || addr < alt_group->first_insn->offset || addr > alt_group->last_insn->offset) return false; orig_first_insn = alt_group->orig_group->first_insn; offset = addr - alt_group->first_insn->offset; addr = orig_first_insn->offset + offset; sym = insn_sym(orig_first_insn); disas_print_addr_sym(orig_first_insn->sec, sym, addr, dinfo); return true; } static void disas_print_addr_noreloc(bfd_vma addr, struct disassemble_info *dinfo) { struct disas_context *dctx = dinfo->application_data; struct instruction *insn = dctx->insn; struct symbol *sym = insn_sym(insn); if (disas_print_addr_alt(addr, dinfo)) return; if (sym && (addr < sym->offset || addr >= sym->offset + sym->len)) sym = NULL; disas_print_addr_sym(insn->sec, sym, addr, dinfo); } static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo) { struct disas_context *dctx = dinfo->application_data; struct instruction *insn = dctx->insn; unsigned long offset; struct reloc *reloc; char symstr[1024]; char *str; reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec, insn->offset, insn->len); if (!reloc) { /* * There is no relocation for this instruction although * the address to resolve points to the next instruction. * So this is an effective reference to the next IP, for * example: "lea 0x0(%rip),%rdi". The kernel can reference * the next IP with _THIS_IP_ macro. */ DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, "_THIS_IP_"); return; } offset = arch_insn_adjusted_addend(insn, reloc); /* * If the relocation symbol is a section name (for example ".bss") * then we try to further resolve the name. */ if (is_sec_sym(reloc->sym)) { str = offstr(reloc->sym->sec, reloc->sym->offset + offset); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str); free(str); } else { sprint_name(symstr, reloc->sym->name, offset); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, symstr); } } /* * Resolve an address into a "<symbol>+<offset>" string. */ static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo) { struct disas_context *dctx = dinfo->application_data; struct instruction *insn = dctx->insn; struct instruction *jump_dest; struct symbol *sym; bool is_reloc; /* * If the instruction is a call/jump and it references a * destination then this is likely the address we are looking * up. So check it first. */ jump_dest = insn->jump_dest; if (jump_dest && insn_sym(jump_dest) && jump_dest->offset == addr) { if (!disas_print_addr_alt(addr, dinfo)) disas_print_addr_sym(jump_dest->sec, insn_sym(jump_dest), addr, dinfo); return; } /* * If the address points to the next instruction then there is * probably a relocation. It can be a false positive when the * current instruction is referencing the address of the next * instruction. This particular case will be handled in * disas_print_addr_reloc(). */ is_reloc = (addr == insn->offset + insn->len); /* * The call destination offset can be the address we are looking * up, or 0 if there is a relocation. */ sym = insn_call_dest(insn); if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) { DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, sym->name); return; } if (!is_reloc) disas_print_addr_noreloc(addr, dinfo); else disas_print_addr_reloc(addr, dinfo); } /* * Initialize disassemble info arch, mach (32 or 64-bit) and options. */ int disas_info_init(struct disassemble_info *dinfo, int arch, int mach32, int mach64, const char *options) { struct disas_context *dctx = dinfo->application_data; struct objtool_file *file = dctx->file; dinfo->arch = arch; switch (file->elf->ehdr.e_ident[EI_CLASS]) { case ELFCLASS32: dinfo->mach = mach32; break; case ELFCLASS64: dinfo->mach = mach64; break; default: return -1; } dinfo->disassembler_options = options; return 0; } struct disas_context *disas_context_create(struct objtool_file *file) { struct disas_context *dctx; struct disassemble_info *dinfo; int err; dctx = malloc(sizeof(*dctx)); if (!dctx) { WARN("failed to allocate disassembly context"); return NULL; } dctx->file = file; dinfo = &dctx->info; init_disassemble_info_compat(dinfo, dctx, disas_fprintf, disas_fprintf_styled); dinfo->read_memory_func = buffer_read_memory; dinfo->print_address_func = disas_print_address; dinfo->application_data = dctx; /* * bfd_openr() is not used to avoid doing ELF data processing * and caching that has already being done. Here, we just need * to identify the target file so we call an arch specific * function to fill some disassemble info (arch, mach). */ dinfo->arch = bfd_arch_unknown; dinfo->mach = 0; err = arch_disas_info_init(dinfo); if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) { WARN("failed to init disassembly arch"); goto error; } dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; disassemble_init_for_target(dinfo); dctx->disassembler = disassembler(dinfo->arch, dinfo->endian == BFD_ENDIAN_BIG, dinfo->mach, NULL); if (!dctx->disassembler) { WARN("failed to create disassembler function"); goto error; } return dctx; error: free(dctx); return NULL; } void disas_context_destroy(struct disas_context *dctx) { free(dctx); } char *disas_result(struct disas_context *dctx) { return dctx->result; } #define DISAS_INSN_OFFSET_SPACE 10 #define DISAS_INSN_SPACE 60 #define DISAS_PRINSN(dctx, insn, depth) \ disas_print_insn(stdout, dctx, insn, depth, "\n") /* * Print a message in the instruction flow. If sec is not NULL then the * address at the section offset is printed in addition of the message, * otherwise only the message is printed. */ static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset, int depth, const char *format, va_list ap) { const char *addr_str; int i, n; int len; len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE; if (depth < 0) { len += depth; depth = 0; } n = 0; if (sec) { addr_str = offstr(sec, offset); n += fprintf(stream, "%6lx: %-*s ", offset, len, addr_str); free((char *)addr_str); } else { len += DISAS_INSN_OFFSET_SPACE + 1; n += fprintf(stream, "%-*s", len, ""); } /* print vertical bars to show the code flow */ for (i = 0; i < depth; i++) n += fprintf(stream, "| "); if (format) n += vfprintf(stream, format, ap); return n; } static int disas_print(FILE *stream, struct section *sec, unsigned long offset, int depth, const char *format, ...) { va_list args; int len; va_start(args, format); len = disas_vprint(stream, sec, offset, depth, format, args); va_end(args); return len; } /* * Print a message in the instruction flow. If insn is not NULL then * the instruction address is printed in addition of the message, * otherwise only the message is printed. In all cases, the instruction * itself is not printed. */ void disas_print_info(FILE *stream, struct instruction *insn, int depth, const char *format, ...) { struct section *sec; unsigned long off; va_list args; if (insn) { sec = insn->sec; off = insn->offset; } else { sec = NULL; off = 0; } va_start(args, format); disas_vprint(stream, sec, off, depth, format, args);