1 /*
2 Copyright (C) 2001-2015, Parrot Foundation.
3 
4 =head1 NAME
5 
6 pbc_dump - Dump or convert Parrot bytecode (PBC) files
7 
8 =head1 SYNOPSIS
9 
10  pbc_dump [-tdh] [--terse|--disassemble|--header-only] file.pbc
11 
12  pbc_dump -o converted.pbc file.pbc
13 
14 =head1 DESCRIPTION
15 
16 A program to dump pack files to human readable form.
17 
18 =head2 Command-Line Options
19 
20 =over 4
21 
22 =item C<-d>
23 
24 Disassemble bytecode segments.
25 
26 =item C<-h>
27 
28 Dump the bytecode header only.
29 
30 =item C<-t>
31 
32 Terse output.
33 
34 =item C<-o converted.pbc>
35 
36 Repacks a PBC file into the platform's native binary format for better
37 efficiency on reading non-native PBCs.
38 
39 =back
40 
41 =head2 Functions
42 
43 =over 4
44 
45 =cut
46 
47 */
48 
49 #include "parrot/parrot.h"
50 #include "parrot/longopt.h"
51 #include "parrot/oplib/ops.h"
52 #include "parrot/oplib/core_ops.h"
53 
54 /* HEADERIZER HFILE: none */
55 /* HEADERIZER BEGIN: static */
56 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
57 
58 static void const_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *segp))
59         __attribute__nonnull__(1)
60         __attribute__nonnull__(2);
61 
62 static void disas_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self))
63         __attribute__nonnull__(1)
64         __attribute__nonnull__(2);
65 
66 static void help(void);
67 static void null_dir_dump(PARROT_INTERP,
68     ARGIN(const PackFile_Segment *self))
69         __attribute__nonnull__(1)
70         __attribute__nonnull__(2);
71 
72 static void null_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self))
73         __attribute__nonnull__(2);
74 
75 static void nums_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self))
76         __attribute__nonnull__(1)
77         __attribute__nonnull__(2);
78 
79 static void PackFile_header_dump(PARROT_INTERP, ARGIN(const PackFile *pf))
80         __attribute__nonnull__(1)
81         __attribute__nonnull__(2);
82 
83 #define ASSERT_ARGS_const_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
84        PARROT_ASSERT_ARG(interp) \
85     , PARROT_ASSERT_ARG(segp))
86 #define ASSERT_ARGS_disas_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
87        PARROT_ASSERT_ARG(interp) \
88     , PARROT_ASSERT_ARG(self))
89 #define ASSERT_ARGS_help __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
90 #define ASSERT_ARGS_null_dir_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
91        PARROT_ASSERT_ARG(interp) \
92     , PARROT_ASSERT_ARG(self))
93 #define ASSERT_ARGS_null_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
94        PARROT_ASSERT_ARG(self))
95 #define ASSERT_ARGS_nums_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
96        PARROT_ASSERT_ARG(interp) \
97     , PARROT_ASSERT_ARG(self))
98 #define ASSERT_ARGS_PackFile_header_dump __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
99        PARROT_ASSERT_ARG(interp) \
100     , PARROT_ASSERT_ARG(pf))
101 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
102 /* HEADERIZER END: static */
103 
104 /*
105 
106 =item C<static void const_dump(PARROT_INTERP, const PackFile_Segment *segp)>
107 
108 Dump the constant table.
109 
110 =cut
111 
112 */
113 
114 static void
const_dump(PARROT_INTERP,ARGIN (const PackFile_Segment * segp))115 const_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *segp))
116 {
117     ASSERT_ARGS(const_dump)
118     Parrot_io_printf(interp, "%Ss => [\n", segp->name);
119     pf_const_dump(interp, (const PackFile_ConstTable *)segp);
120     Parrot_io_printf(interp, "],\n");
121 }
122 
123 
124 /*
125 
126 =item C<static void disas_dump(PARROT_INTERP, const PackFile_Segment *self)>
127 
128 Disassemble and dump.
129 
130 =cut
131 
132 */
133 
134 static void
disas_dump(PARROT_INTERP,ARGIN (const PackFile_Segment * self))135 disas_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self))
136 {
137     ASSERT_ARGS(disas_dump)
138     const opcode_t *pc = self->data;
139     const PackFile_ByteCode_OpMapping * const map = &((const PackFile_ByteCode *)self)->op_mapping;
140     INTVAL i;
141 
142     Parrot_io_printf(interp, "%Ss => [ # %d ops at offs 0x%x\n",
143             self->name, (int)self->size, (int)self->file_offset + 4);
144 
145     for (i = 0; i < map->n_libs; i++) {
146 
147         INTVAL j;
148         const PackFile_ByteCode_OpMappingEntry * const entry = &map->libs[i];
149         Parrot_io_printf(interp, "  map #"INTVAL_FMT" => [\n", i);
150         Parrot_io_printf(interp, "    oplib: \"%s\" version %d.%d (%ld ops)\n",
151                 entry->lib->name,
152                 entry->lib->bc_major_version,
153                 entry->lib->bc_minor_version,
154                 entry->n_ops);
155 
156         for (j = 0; j < map->libs[i].n_ops; j++) {
157             const INTVAL lib_num   = entry->lib_ops[j];
158             const INTVAL table_num = entry->table_ops[j];
159             Parrot_io_printf(interp, "    %08lx => %08lx (%s)\n", table_num, lib_num,
160                     entry->lib->op_info_table[lib_num].full_name);
161         }
162         Parrot_io_printf(interp, "  ]\n");
163     }
164 
165     while (pc < self->data + self->size) {
166         /* n can't be const; the ADD_OP_VAR_PART macro increments it */
167         size_t n = (size_t)interp->code->op_info_table[*pc]->op_count;
168         size_t j;
169 
170         /* trace_op_dump(interp, self->pf->src, pc); */
171         Parrot_io_printf(interp, " %04x:  ", (int)(pc - self->data));
172 
173         for (j = 0; j < 6; ++j) {
174             if (j < n)
175                 Parrot_io_printf(interp, "%08lx ", (unsigned long)pc[j]);
176             else
177                 Parrot_io_printf(interp, "         ");
178         }
179 
180         Parrot_io_printf(interp, "%s\n",
181                 interp->code->op_info_table[*pc]->full_name);
182 
183         ADD_OP_VAR_PART(interp, interp->code, pc, n);
184         pc += n;
185     }
186 
187     Parrot_io_printf(interp, "]\n");
188 }
189 
190 
191 /*
192 
193 =item C<static void nums_dump(PARROT_INTERP, const PackFile_Segment *self)>
194 
195 Disassembles and dumps op names and line numbers only.
196 
197 =cut
198 
199 */
200 
201 static void
nums_dump(PARROT_INTERP,ARGIN (const PackFile_Segment * self))202 nums_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self))
203 {
204     ASSERT_ARGS(nums_dump)
205     const STRING           *debug_name = Parrot_str_concat(interp, self->name,
206             Parrot_str_new_constant(interp, "_DB"));
207     const PackFile_Segment *debug      = Parrot_pf_find_segment(interp,
208                                             self->dir, debug_name, 1);
209 
210     opcode_t   * pc            = self->data;
211     opcode_t   * debug_ops     = debug->data;
212     op_info_t ** const op_info = interp->code->op_info_table;
213 
214     while (pc < self->data + self->size) {
215         /* n can't be const; the ADD_OP_VAR_PART macro increments it */
216         size_t n = (size_t)op_info[*pc]->op_count;
217 
218         Parrot_io_printf(interp, " %04lx:  %s\n",
219             *(debug_ops++), op_info[*pc]->full_name);
220 
221         ADD_OP_VAR_PART(interp, interp->code, pc, n);
222         pc += n;
223     }
224 }
225 
226 
227 /*
228 
229 =item C<static void null_dump(PARROT_INTERP, const PackFile_Segment *self)>
230 
231 Produces no output for the given segment type.
232 
233 =cut
234 
235 */
236 
237 static void
null_dump(SHIM_INTERP,ARGIN (const PackFile_Segment * self))238 null_dump(SHIM_INTERP, ARGIN(const PackFile_Segment *self))
239 {
240     ASSERT_ARGS(null_dump)
241     UNUSED(self);
242 }
243 
244 
245 /*
246 
247 =item C<static void null_dir_dump(PARROT_INTERP, const PackFile_Segment *self)>
248 
249 Dumps all of the segments of the given PackFile_Directory, but produces no
250 output for the directory itself.
251 
252 =cut
253 
254 */
255 
256 static void
null_dir_dump(PARROT_INTERP,ARGIN (const PackFile_Segment * self))257 null_dir_dump(PARROT_INTERP, ARGIN(const PackFile_Segment *self))
258 {
259     ASSERT_ARGS(null_dir_dump)
260     const PackFile_Directory * const dir = (const PackFile_Directory *)self;
261     size_t i;
262 
263     for (i = 0; i < dir->num_segments; ++i)
264         self->pf->PackFuncs[dir->segments[i]->type].dump(interp, dir->segments[i]);
265 }
266 
267 
268 /*
269 
270 =item C<static void PackFile_header_dump(PARROT_INTERP, const PackFile *pf)>
271 
272 Dump the header.
273 
274 =cut
275 
276 */
277 
278 static void
PackFile_header_dump(PARROT_INTERP,ARGIN (const PackFile * pf))279 PackFile_header_dump(PARROT_INTERP, ARGIN(const PackFile *pf))
280 {
281     ASSERT_ARGS(PackFile_header_dump)
282     const PackFile_Header * const header = pf->header;
283 
284     Parrot_io_printf(interp, "HEADER => [\n");
285     Parrot_io_printf(interp, "\twordsize  = %d", header->wordsize);
286     Parrot_io_printf(interp, "\t(interpreter's wordsize/INTVAL = %lu/%lu)\n",
287                      sizeof (opcode_t), sizeof (INTVAL));
288     Parrot_io_printf(interp, "\tbyteorder = %d", header->byteorder);
289     Parrot_io_printf(interp, "\t(interpreter's byteorder       = %d)\n",
290             PARROT_BIGENDIAN);
291     Parrot_io_printf(interp, "\tfloattype = %d", header->floattype);
292     Parrot_io_printf(interp, "\t(interpreter's NUMVAL_SIZE     = %d)\n",
293             NUMVAL_SIZE);
294     Parrot_io_printf(interp, "\tparrot-version %d.%d.%d, "
295             "bytecode-version %d.%d\n",
296             header->major, header->minor, header->patch,
297             header->bc_major, header->bc_minor);
298 
299     Parrot_io_printf(interp, "\tUUID: type = %d, size = %d",
300             header->uuid_type, header->uuid_size);
301 
302     if (header->uuid_size > 0)
303         Parrot_io_printf(interp, ", '%s'\n", header->uuid_data);
304     else
305         Parrot_io_printf(interp, "\n");
306 
307     Parrot_io_printf(interp, "\t%s endianize, %s opcode, %s numval transform\n",
308             pf->need_endianize ? "**need**" : "no",
309             pf->need_wordsize  ? "**need**" : "no",
310             pf->fetch_nv       ? "**need**" : "no");
311 
312     Parrot_io_printf(interp, "\tdirformat = %ld\n", header->dir_format);
313     Parrot_io_printf(interp, "]\n");
314 }
315 
316 
317 /*
318 
319 =item C<static void help(void)>
320 
321 Print out the user help info.
322 
323 =cut
324 
325 */
326 
327 static void
help(void)328 help(void)
329 {
330     printf("pbc_dump - dump or convert parrot bytecode (PBC) files\n");
331     printf("usage:\n");
332     printf("pbc_dump [-tdh] [--terse|--disassemble|--header-only|--line-nums]"
333            " file.pbc\n");
334     printf("pbc_dump -o converted.pbc file.pbc\n\n");
335     printf("\t-d ... disassemble bytecode segments\n");
336     printf("\t-h ... dump header only\n");
337     printf("\t-t ... terse output\n");
338     printf("\t-n ... show ops and line numbers only\n");
339 
340     printf("\t-o converted.pbc ... repacks a PBC file into "
341            "the platform's native\n");
342     printf("\t   binary format for better efficiency on reading "
343            "non native PBCs\n");
344     exit(EXIT_SUCCESS);
345 }
346 
347 
348 static struct longopt_opt_decl opt_options[] = {
349     { 'h', 'h', OPTION_optional_FLAG, { "--header-only" } },
350     { '?', '?', OPTION_optional_FLAG, { "--help"        } },
351     { 't', 't', OPTION_optional_FLAG, { "--terse"       } },
352     { 'n', 'n', OPTION_optional_FLAG, { "--line-nums"   } },
353     { 'd', 'd', OPTION_optional_FLAG, { "--disassemble" } },
354     { 'o', 'o', OPTION_required_FLAG, { "--output"      } },
355     { 0,    0,  OPTION_optional_FLAG, { NULL            } }
356 };
357 
358 
359 /*
360 
361 =item C<int main(int argc, const char **argv)>
362 
363 The run loop. Process the command-line arguments and dump accordingly.
364 
365 =cut
366 
367 */
368 
369 int
main(int argc,const char ** argv)370 main(int argc, const char **argv)
371 {
372     Parrot_PackFile  pfpmc;
373     PackFile        *pf;
374     Interp          *interp;
375     Parrot_String   infilename;
376 
377     const char *file            = NULL;
378     int         terse           = 0;
379     int         disas           = 0;
380     int         convert         = 0;
381     int         nums_only       = 0;
382     int         options         = PFOPT_UTILS;
383 
384     struct longopt_opt_info opt = LONGOPT_OPT_INFO_INIT;
385 
386     int         status;
387 
388     if (argc < 2)
389         help();
390 
391     interp = Parrot_interp_new(NULL);
392 
393     /* init and set top of stack */
394     Parrot_interp_init_stacktop(interp, &status);
395 
396     while ((status = Parrot_longopt_get(argc, argv, opt_options, &opt)) > 0) {
397         switch (opt.opt_id) {
398           case 'h':
399             options += PFOPT_HEADERONLY;
400             break;
401           case 't':
402             terse = 1;
403             break;
404           case 'd':
405             disas = 1;
406             break;
407           case 'o':
408             file    = opt.opt_arg;
409             convert = 1;
410             break;
411           case 'n':
412             nums_only = 1;
413             break;
414           case '?':
415           default:
416             help();
417             break;
418         }
419     }
420 
421     if (status == -1)
422         help();
423 
424     argc -= opt.opt_index;
425     argv += opt.opt_index;
426 
427     infilename = Parrot_str_new(interp, *argv, 0);
428     pf = Parrot_pf_read_pbc_file(interp, infilename);
429 
430     if (pf == NULL) {
431         printf("Can't read PBC\n");
432         return 1;
433     }
434 
435     if (options & PFOPT_HEADERONLY) {
436         PackFile_header_dump(interp, pf);
437         Parrot_x_exit(interp, 0);
438     }
439 
440     pfpmc = Parrot_pf_get_packfile_pmc(interp, pf, infilename);
441     Parrot_pf_set_current_packfile(interp, pfpmc);
442 
443     if (convert) {
444         const size_t size = Parrot_pf_pack_size(interp,
445                             interp->code->base.pf) * sizeof (opcode_t);
446         opcode_t *pack = (opcode_t *)Parrot_gc_allocate_memory_chunk(interp,
447                                         size);
448         FILE *fp;
449 
450         if (!pack) {
451             printf("out of mem\n");
452             exit(EXIT_FAILURE);
453         }
454 
455         Parrot_pf_pack(interp, interp->code->base.pf, pack);
456 
457         if (STREQ(file, "-"))
458             fp = stdout;
459         else if ((fp = fopen(file, "wb")) == 0) {
460             printf("Couldn't open %s\n", file);
461             exit(EXIT_FAILURE);
462         }
463 
464         if ((1 != fwrite(pack, size, 1, fp))) {
465             printf("Couldn't write %s\n", file);
466             exit(EXIT_FAILURE);
467         }
468 
469         fclose(fp);
470         Parrot_gc_free_memory_chunk(interp, pack);
471         Parrot_x_exit(interp, 0);
472     }
473 
474     if (!nums_only)
475         PackFile_header_dump(interp, pf);
476 
477     if (options & PFOPT_HEADERONLY)
478         Parrot_x_exit(interp, 0);
479 
480     /* install a dumper function */
481     if (!terse) {
482         pf->PackFuncs[PF_CONST_SEG].dump = const_dump;
483     }
484 
485     if (disas)
486         pf->PackFuncs[PF_BYTEC_SEG].dump = disas_dump;
487 
488     if (nums_only) {
489         int i;
490 
491         for (i = PF_DIR_SEG + 1; i < PF_MAX_SEG; ++i)
492             pf->PackFuncs[i].dump = null_dump;
493 
494         pf->PackFuncs[PF_DIR_SEG].dump   = null_dir_dump;
495         pf->PackFuncs[PF_BYTEC_SEG].dump = nums_dump;
496     }
497 
498     /* do a directory dump, which dumps segs then */
499     Parrot_pf_dump_segment(interp, &pf->directory.base);
500 
501     Parrot_x_exit(interp, 0);
502 }
503 
504 
505 /*
506 
507 =back
508 
509 =head1 SEE ALSO
510 
511 F<src/packdump.c>.
512 
513 =cut
514 
515 */
516 
517 /*
518  * Local variables:
519  *   c-file-style: "parrot"
520  * End:
521  * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
522  */
523