/* Copyright (C) 2001-2015, Parrot Foundation. =head1 NAME pbc_dump - Dump or convert Parrot bytecode (PBC) files =head1 SYNOPSIS pbc_dump [-tdh] [--terse|--disassemble|--header-only] file.pbc pbc_dump -o converted.pbc file.pbc =head1 DESCRIPTION A program to dump pack files to human readable form. =head2 Command-Line Options =over 4 =item C<-d> Disassemble bytecode segments. =item C<-h> Dump the bytecode header only. =item C<-t> Terse output. =item C<-o converted.pbc> Repacks a PBC file into the platform's native binary format for better efficiency on reading non-native PBCs. =back =head2 Functions =over 4 =cut */ #include "parrot/parrot.h" #include "parrot/longopt.h" #include "parrot/oplib/ops.h" #include "parrot/oplib/core_ops.h" /* HEADERIZER HFILE: none */ /* HEADERIZER BEGIN: static */ /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */ static void const_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *segp)) __attribute__nonnull__(1) __attribute__nonnull__(2); static void disas_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self)) __attribute__nonnull__(1) __attribute__nonnull__(2); static void help(void); static void null_dir_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self)) __attribute__nonnull__(1) __attribute__nonnull__(2); static void null_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self)) __attribute__nonnull__(2); static void nums_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self)) __attribute__nonnull__(1) __attribute__nonnull__(2); static void PackFile_header_dump(PARROT_INTERP, ARGIN(const PackFile *pf)) __attribute__nonnull__(1) __attribute__nonnull__(2); #define ASSERT_ARGS_const_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ , PARROT_ASSERT_ARG(segp)) #define ASSERT_ARGS_disas_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ , PARROT_ASSERT_ARG(self)) #define ASSERT_ARGS_help __attribute__unused__ int _ASSERT_ARGS_CHECK = (0) #define ASSERT_ARGS_null_dir_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ , PARROT_ASSERT_ARG(self)) #define ASSERT_ARGS_null_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(self)) #define ASSERT_ARGS_nums_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ , PARROT_ASSERT_ARG(self)) #define ASSERT_ARGS_PackFile_header_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ , PARROT_ASSERT_ARG(pf)) /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */ /* HEADERIZER END: static */ /* =item C Dump the constant table. =cut */ static void const_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *segp)) { ASSERT_ARGS(const_dump) Parrot_io_printf(interp, "%Ss => [\n", segp->name); pf_const_dump(interp, (const PackFile_ConstTable *)segp); Parrot_io_printf(interp, "],\n"); } /* =item C Disassemble and dump. =cut */ static void disas_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self)) { ASSERT_ARGS(disas_dump) const opcode_t *pc = self->data; const PackFile_ByteCode_OpMapping * const map = &((const PackFile_ByteCode *)self)->op_mapping; INTVAL i; Parrot_io_printf(interp, "%Ss => [ # %d ops at offs 0x%x\n", self->name, (int)self->size, (int)self->file_offset + 4); for (i = 0; i < map->n_libs; i++) { INTVAL j; const PackFile_ByteCode_OpMappingEntry * const entry = &map->libs[i]; Parrot_io_printf(interp, " map #"INTVAL_FMT" => [\n", i); Parrot_io_printf(interp, " oplib: \"%s\" version %d.%d (%ld ops)\n", entry->lib->name, entry->lib->bc_major_version, entry->lib->bc_minor_version, entry->n_ops); for (j = 0; j < map->libs[i].n_ops; j++) { const INTVAL lib_num = entry->lib_ops[j]; const INTVAL table_num = entry->table_ops[j]; Parrot_io_printf(interp, " %08lx => %08lx (%s)\n", table_num, lib_num, entry->lib->op_info_table[lib_num].full_name); } Parrot_io_printf(interp, " ]\n"); } while (pc < self->data + self->size) { /* n can't be const; the ADD_OP_VAR_PART macro increments it */ size_t n = (size_t)interp->code->op_info_table[*pc]->op_count; size_t j; /* trace_op_dump(interp, self->pf->src, pc); */ Parrot_io_printf(interp, " %04x: ", (int)(pc - self->data)); for (j = 0; j < 6; ++j) { if (j < n) Parrot_io_printf(interp, "%08lx ", (unsigned long)pc[j]); else Parrot_io_printf(interp, " "); } Parrot_io_printf(interp, "%s\n", interp->code->op_info_table[*pc]->full_name); ADD_OP_VAR_PART(interp, interp->code, pc, n); pc += n; } Parrot_io_printf(interp, "]\n"); } /* =item C Disassembles and dumps op names and line numbers only. =cut */ static void nums_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self)) { ASSERT_ARGS(nums_dump) const STRING *debug_name = Parrot_str_concat(interp, self->name, Parrot_str_new_constant(interp, "_DB")); const PackFile_Segment *debug = Parrot_pf_find_segment(interp, self->dir, debug_name, 1); opcode_t * pc = self->data; opcode_t * debug_ops = debug->data; op_info_t ** const op_info = interp->code->op_info_table; while (pc < self->data + self->size) { /* n can't be const; the ADD_OP_VAR_PART macro increments it */ size_t n = (size_t)op_info[*pc]->op_count; Parrot_io_printf(interp, " %04lx: %s\n", *(debug_ops++), op_info[*pc]->full_name); ADD_OP_VAR_PART(interp, interp->code, pc, n); pc += n; } } /* =item C Produces no output for the given segment type. =cut */ static void null_dump(SHIM_INTERP, ARGIN(const PackFile_Segment *self)) { ASSERT_ARGS(null_dump) UNUSED(self); } /* =item C Dumps all of the segments of the given PackFile_Directory, but produces no output for the directory itself. =cut */ static void null_dir_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self)) { ASSERT_ARGS(null_dir_dump) const PackFile_Directory * const dir = (const PackFile_Directory *)self; size_t i; for (i = 0; i < dir->num_segments; ++i) self->pf->PackFuncs[dir->segments[i]->type].dump(interp, dir->segments[i]); } /* =item C Dump the header. =cut */ static void PackFile_header_dump(PARROT_INTERP, ARGIN(const PackFile *pf)) { ASSERT_ARGS(PackFile_header_dump) const PackFile_Header * const header = pf->header; Parrot_io_printf(interp, "HEADER => [\n"); Parrot_io_printf(interp, "\twordsize = %d", header->wordsize); Parrot_io_printf(interp, "\t(interpreter's wordsize/INTVAL = %lu/%lu)\n", sizeof (opcode_t), sizeof (INTVAL)); Parrot_io_printf(interp, "\tbyteorder = %d", header->byteorder); Parrot_io_printf(interp, "\t(interpreter's byteorder = %d)\n", PARROT_BIGENDIAN); Parrot_io_printf(interp, "\tfloattype = %d", header->floattype); Parrot_io_printf(interp, "\t(interpreter's NUMVAL_SIZE = %d)\n", NUMVAL_SIZE); Parrot_io_printf(interp, "\tparrot-version %d.%d.%d, " "bytecode-version %d.%d\n", header->major, header->minor, header->patch, header->bc_major, header->bc_minor); Parrot_io_printf(interp, "\tUUID: type = %d, size = %d", header->uuid_type, header->uuid_size); if (header->uuid_size > 0) Parrot_io_printf(interp, ", '%s'\n", header->uuid_data); else Parrot_io_printf(interp, "\n"); Parrot_io_printf(interp, "\t%s endianize, %s opcode, %s numval transform\n", pf->need_endianize ? "**need**" : "no", pf->need_wordsize ? "**need**" : "no", pf->fetch_nv ? "**need**" : "no"); Parrot_io_printf(interp, "\tdirformat = %ld\n", header->dir_format); Parrot_io_printf(interp, "]\n"); } /* =item C Print out the user help info. =cut */ static void help(void) { printf("pbc_dump - dump or convert parrot bytecode (PBC) files\n"); printf("usage:\n"); printf("pbc_dump [-tdh] [--terse|--disassemble|--header-only|--line-nums]" " file.pbc\n"); printf("pbc_dump -o converted.pbc file.pbc\n\n"); printf("\t-d ... disassemble bytecode segments\n"); printf("\t-h ... dump header only\n"); printf("\t-t ... terse output\n"); printf("\t-n ... show ops and line numbers only\n"); printf("\t-o converted.pbc ... repacks a PBC file into " "the platform's native\n"); printf("\t binary format for better efficiency on reading " "non native PBCs\n"); exit(EXIT_SUCCESS); } static struct longopt_opt_decl opt_options[] = { { 'h', 'h', OPTION_optional_FLAG, { "--header-only" } }, { '?', '?', OPTION_optional_FLAG, { "--help" } }, { 't', 't', OPTION_optional_FLAG, { "--terse" } }, { 'n', 'n', OPTION_optional_FLAG, { "--line-nums" } }, { 'd', 'd', OPTION_optional_FLAG, { "--disassemble" } }, { 'o', 'o', OPTION_required_FLAG, { "--output" } }, { 0, 0, OPTION_optional_FLAG, { NULL } } }; /* =item C The run loop. Process the command-line arguments and dump accordingly. =cut */ int main(int argc, const char **argv) { Parrot_PackFile pfpmc; PackFile *pf; Interp *interp; Parrot_String infilename; const char *file = NULL; int terse = 0; int disas = 0; int convert = 0; int nums_only = 0; int options = PFOPT_UTILS; struct longopt_opt_info opt = LONGOPT_OPT_INFO_INIT; int status; if (argc < 2) help(); interp = Parrot_interp_new(NULL); /* init and set top of stack */ Parrot_interp_init_stacktop(interp, &status); while ((status = Parrot_longopt_get(argc, argv, opt_options, &opt)) > 0) { switch (opt.opt_id) { case 'h': options += PFOPT_HEADERONLY; break; case 't': terse = 1; break; case 'd': disas = 1; break; case 'o': file = opt.opt_arg; convert = 1; break; case 'n': nums_only = 1; break; case '?': default: help(); break; } } if (status == -1) help(); argc -= opt.opt_index; argv += opt.opt_index; infilename = Parrot_str_new(interp, *argv, 0); pf = Parrot_pf_read_pbc_file(interp, infilename); if (pf == NULL) { printf("Can't read PBC\n"); return 1; } if (options & PFOPT_HEADERONLY) { PackFile_header_dump(interp, pf); Parrot_x_exit(interp, 0); } pfpmc = Parrot_pf_get_packfile_pmc(interp, pf, infilename); Parrot_pf_set_current_packfile(interp, pfpmc); if (convert) { const size_t size = Parrot_pf_pack_size(interp, interp->code->base.pf) * sizeof (opcode_t); opcode_t *pack = (opcode_t *)Parrot_gc_allocate_memory_chunk(interp, size); FILE *fp; if (!pack) { printf("out of mem\n"); exit(EXIT_FAILURE); } Parrot_pf_pack(interp, interp->code->base.pf, pack); if (STREQ(file, "-")) fp = stdout; else if ((fp = fopen(file, "wb")) == 0) { printf("Couldn't open %s\n", file); exit(EXIT_FAILURE); } if ((1 != fwrite(pack, size, 1, fp))) { printf("Couldn't write %s\n", file); exit(EXIT_FAILURE); } fclose(fp); Parrot_gc_free_memory_chunk(interp, pack); Parrot_x_exit(interp, 0); } if (!nums_only) PackFile_header_dump(interp, pf); if (options & PFOPT_HEADERONLY) Parrot_x_exit(interp, 0); /* install a dumper function */ if (!terse) { pf->PackFuncs[PF_CONST_SEG].dump = const_dump; } if (disas) pf->PackFuncs[PF_BYTEC_SEG].dump = disas_dump; if (nums_only) { int i; for (i = PF_DIR_SEG + 1; i < PF_MAX_SEG; ++i) pf->PackFuncs[i].dump = null_dump; pf->PackFuncs[PF_DIR_SEG].dump = null_dir_dump; pf->PackFuncs[PF_BYTEC_SEG].dump = nums_dump; } /* do a directory dump, which dumps segs then */ Parrot_pf_dump_segment(interp, &pf->directory.base); Parrot_x_exit(interp, 0); } /* =back =head1 SEE ALSO F. =cut */ /* * Local variables: * c-file-style: "parrot" * End: * vim: expandtab shiftwidth=4 cinoptions='\:2=2' : */