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