@@ -265,7 +265,10 @@ fn make(step: *Step) !void {
265
265
}),
266
266
.elf = > @panic ("TODO elf parser" ),
267
267
.coff = > @panic ("TODO coff parser" ),
268
- .wasm = > @panic ("TODO wasm parser" ),
268
+ .wasm = > try WasmDumper .parseAndDump (contents , .{
269
+ .gpa = gpa ,
270
+ .dump_symtab = self .dump_symtab ,
271
+ }),
269
272
else = > unreachable ,
270
273
};
271
274
@@ -522,3 +525,295 @@ const MachODumper = struct {
522
525
}
523
526
}
524
527
};
528
+
529
+ const WasmDumper = struct {
530
+ const symtab_label = "symbols" ;
531
+
532
+ fn parseAndDump (bytes : []const u8 , opts : Opts ) ! []const u8 {
533
+ const gpa = opts .gpa orelse unreachable ; // Wasm dumper requires an allocator
534
+ if (opts .dump_symtab ) {
535
+ @panic ("TODO: Implement symbol table parsing and dumping" );
536
+ }
537
+
538
+ var fbs = std .io .fixedBufferStream (bytes );
539
+ const reader = fbs .reader ();
540
+
541
+ const buf = try reader .readBytesNoEof (8 );
542
+ if (! mem .eql (u8 , buf [0.. 4], & std .wasm .magic )) {
543
+ return error .InvalidMagicByte ;
544
+ }
545
+ if (! mem .eql (u8 , buf [4.. ], & std .wasm .version )) {
546
+ return error .UnsupportedWasmVersion ;
547
+ }
548
+
549
+ var output = std .ArrayList (u8 ).init (gpa );
550
+ errdefer output .deinit ();
551
+ const writer = output .writer ();
552
+
553
+ while (reader .readByte ()) | current_byte | {
554
+ const section = std .meta .intToEnum (std .wasm .Section , current_byte ) catch | err | {
555
+ std .debug .print ("Found invalid section id '{d}'\n " , .{current_byte });
556
+ return err ;
557
+ };
558
+
559
+ const section_length = try std .leb .readULEB128 (u32 , reader );
560
+ try parseAndDumpSection (section , bytes [fbs .pos .. ][0.. section_length ], writer );
561
+ fbs .pos += section_length ;
562
+ } else | _ | {} // reached end of stream
563
+
564
+ return output .toOwnedSlice ();
565
+ }
566
+
567
+ fn parseAndDumpSection (section : std.wasm.Section , data : []const u8 , writer : anytype ) ! void {
568
+ var fbs = std .io .fixedBufferStream (data );
569
+ const reader = fbs .reader ();
570
+
571
+ try writer .print (
572
+ \\Section {s}
573
+ \\size {d}
574
+ , .{ @tagName (section ), data .len });
575
+
576
+ switch (section ) {
577
+ .type ,
578
+ .import ,
579
+ .function ,
580
+ .table ,
581
+ .memory ,
582
+ .global ,
583
+ .@"export" ,
584
+ .element ,
585
+ .code ,
586
+ .data ,
587
+ = > {
588
+ const entries = try std .leb .readULEB128 (u32 , reader );
589
+ try writer .print ("\n entries {d}\n " , .{entries });
590
+ try dumpSection (section , data [fbs .pos .. ], entries , writer );
591
+ },
592
+ .custom = > {
593
+ const name_length = try std .leb .readULEB128 (u32 , reader );
594
+ const name = data [fbs .pos .. ][0.. name_length ];
595
+ fbs .pos += name_length ;
596
+ try writer .print ("\n name {s}\n " , .{name });
597
+
598
+ if (mem .eql (u8 , name , "name" )) {
599
+ try parseDumpNames (reader , writer , data );
600
+ }
601
+ // TODO: Implement parsing and dumping other custom sections (such as relocations)
602
+ },
603
+ .start = > {
604
+ const start = try std .leb .readULEB128 (u32 , reader );
605
+ try writer .print ("\n start {d}\n " , .{start });
606
+ },
607
+ else = > {}, // skip unknown sections
608
+ }
609
+ }
610
+
611
+ fn dumpSection (section : std.wasm.Section , data : []const u8 , entries : u32 , writer : anytype ) ! void {
612
+ var fbs = std .io .fixedBufferStream (data );
613
+ const reader = fbs .reader ();
614
+
615
+ switch (section ) {
616
+ .type = > {
617
+ var i : u32 = 0 ;
618
+ while (i < entries ) : (i += 1 ) {
619
+ const func_type = try reader .readByte ();
620
+ if (func_type != std .wasm .function_type ) {
621
+ std .debug .print ("Expected function type, found byte '{d}'\n " , .{func_type });
622
+ return error .UnexpectedByte ;
623
+ }
624
+ const params = try std .leb .readULEB128 (u32 , reader );
625
+ try writer .print ("params {d}\n " , .{params });
626
+ var index : u32 = 0 ;
627
+ while (index < params ) : (index += 1 ) {
628
+ try parseDumpType (std .wasm .Valtype , reader , writer );
629
+ } else index = 0 ;
630
+ const returns = try std .leb .readULEB128 (u32 , reader );
631
+ try writer .print ("returns {d}\n " , .{returns });
632
+ while (index < returns ) : (index += 1 ) {
633
+ try parseDumpType (std .wasm .Valtype , reader , writer );
634
+ }
635
+ }
636
+ },
637
+ .import = > {
638
+ var i : u32 = 0 ;
639
+ while (i < entries ) : (i += 1 ) {
640
+ const module_name_len = try std .leb .readULEB128 (u32 , reader );
641
+ const module_name = data [fbs .pos .. ][0.. module_name_len ];
642
+ fbs .pos += module_name_len ;
643
+ const name_len = try std .leb .readULEB128 (u32 , reader );
644
+ const name = data [fbs .pos .. ][0.. name_len ];
645
+ fbs .pos += name_len ;
646
+
647
+ const kind = std .meta .intToEnum (std .wasm .ExternalKind , try reader .readByte ()) catch | err | {
648
+ std .debug .print ("Invalid import kind\n " , .{});
649
+ return err ;
650
+ };
651
+
652
+ try writer .print (
653
+ \\module {s}
654
+ \\name {s}
655
+ \\kind {s}
656
+ , .{ module_name , name , @tagName (kind ) });
657
+ try writer .writeByte ('\n ' );
658
+ switch (kind ) {
659
+ .function = > {
660
+ try writer .print ("index {d}\n " , .{try std .leb .readULEB128 (u32 , reader )});
661
+ },
662
+ .memory = > {
663
+ try parseDumpLimits (reader , writer );
664
+ },
665
+ .global = > {
666
+ try parseDumpType (std .wasm .Valtype , reader , writer );
667
+ try writer .print ("mutable {}\n " , .{0x01 == try std .leb .readULEB128 (u32 , reader )});
668
+ },
669
+ .table = > {
670
+ try parseDumpType (std .wasm .RefType , reader , writer );
671
+ try parseDumpLimits (reader , writer );
672
+ },
673
+ }
674
+ }
675
+ },
676
+ .function = > {
677
+ var i : u32 = 0 ;
678
+ while (i < entries ) : (i += 1 ) {
679
+ try writer .print ("index {d}\n " , .{try std .leb .readULEB128 (u32 , reader )});
680
+ }
681
+ },
682
+ .table = > {
683
+ var i : u32 = 0 ;
684
+ while (i < entries ) : (i += 1 ) {
685
+ try parseDumpType (std .wasm .RefType , reader , writer );
686
+ try parseDumpLimits (reader , writer );
687
+ }
688
+ },
689
+ .memory = > {
690
+ var i : u32 = 0 ;
691
+ while (i < entries ) : (i += 1 ) {
692
+ try parseDumpLimits (reader , writer );
693
+ }
694
+ },
695
+ .global = > {
696
+ var i : u32 = 0 ;
697
+ while (i < entries ) : (i += 1 ) {
698
+ try parseDumpType (std .wasm .Valtype , reader , writer );
699
+ try writer .print ("mutable {}\n " , .{0x01 == try std .leb .readULEB128 (u1 , reader )});
700
+ try parseDumpInit (reader , writer );
701
+ }
702
+ },
703
+ .@"export" = > {
704
+ var i : u32 = 0 ;
705
+ while (i < entries ) : (i += 1 ) {
706
+ const name_len = try std .leb .readULEB128 (u32 , reader );
707
+ const name = data [fbs .pos .. ][0.. name_len ];
708
+ fbs .pos += name_len ;
709
+ const kind_byte = try std .leb .readULEB128 (u8 , reader );
710
+ const kind = std .meta .intToEnum (std .wasm .ExternalKind , kind_byte ) catch | err | {
711
+ std .debug .print ("invalid export kind value '{d}'\n " , .{kind_byte });
712
+ return err ;
713
+ };
714
+ const index = try std .leb .readULEB128 (u32 , reader );
715
+ try writer .print (
716
+ \\name {s}
717
+ \\kind {s}
718
+ \\index {d}
719
+ , .{ name , @tagName (kind ), index });
720
+ try writer .writeByte ('\n ' );
721
+ }
722
+ },
723
+ .element = > {
724
+ var i : u32 = 0 ;
725
+ while (i < entries ) : (i += 1 ) {
726
+ try writer .print ("table index {d}\n " , .{try std .leb .readULEB128 (u32 , reader )});
727
+ try parseDumpInit (reader , writer );
728
+
729
+ const function_indexes = try std .leb .readULEB128 (u32 , reader );
730
+ var function_index : u32 = 0 ;
731
+ try writer .print ("indexes {d}\n " , .{function_indexes });
732
+ while (function_index < function_indexes ) : (function_index += 1 ) {
733
+ try writer .print ("index {d}\n " , .{try std .leb .readULEB128 (u32 , reader )});
734
+ }
735
+ }
736
+ },
737
+ .code = > {}, // code section is considered opaque to linker
738
+ .data = > {
739
+ var i : u32 = 0 ;
740
+ while (i < entries ) : (i += 1 ) {
741
+ const index = try std .leb .readULEB128 (u32 , reader );
742
+ try writer .print ("memory index 0x{x}\n " , .{index });
743
+ try parseDumpInit (reader , writer );
744
+ const size = try std .leb .readULEB128 (u32 , reader );
745
+ try writer .print ("size {d}\n " , .{size });
746
+ try reader .skipBytes (size , .{}); // we do not care about the content of the segments
747
+ }
748
+ },
749
+ else = > unreachable ,
750
+ }
751
+ }
752
+
753
+ fn parseDumpType (comptime WasmType : type , reader : anytype , writer : anytype ) ! void {
754
+ const type_byte = try reader .readByte ();
755
+ const valtype = std .meta .intToEnum (WasmType , type_byte ) catch | err | {
756
+ std .debug .print ("Invalid wasm type value '{d}'\n " , .{type_byte });
757
+ return err ;
758
+ };
759
+ try writer .print ("type {s}\n " , .{@tagName (valtype )});
760
+ }
761
+
762
+ fn parseDumpLimits (reader : anytype , writer : anytype ) ! void {
763
+ const flags = try std .leb .readULEB128 (u8 , reader );
764
+ const min = try std .leb .readULEB128 (u32 , reader );
765
+
766
+ try writer .print ("min {x}\n " , .{min });
767
+ if (flags != 0 ) {
768
+ try writer .print ("max {x}\n " , .{try std .leb .readULEB128 (u32 , reader )});
769
+ }
770
+ }
771
+
772
+ fn parseDumpInit (reader : anytype , writer : anytype ) ! void {
773
+ const byte = try std .leb .readULEB128 (u8 , reader );
774
+ const opcode = std .meta .intToEnum (std .wasm .Opcode , byte ) catch | err | {
775
+ std .debug .print ("invalid wasm opcode '{d}'\n " , .{byte });
776
+ return err ;
777
+ };
778
+ switch (opcode ) {
779
+ .i32_const = > try writer .print ("i32.const {x}\n " , .{try std .leb .readILEB128 (i32 , reader )}),
780
+ .i64_const = > try writer .print ("i64.const {x}\n " , .{try std .leb .readILEB128 (i64 , reader )}),
781
+ .f32_const = > try writer .print ("f32.const {x}\n " , .{@bitCast (f32 , try reader .readIntLittle (u32 ))}),
782
+ .f64_const = > try writer .print ("f64.const {x}\n " , .{@bitCast (f64 , try reader .readIntLittle (u64 ))}),
783
+ .global_get = > try writer .print ("global.get {x}\n " , .{try std .leb .readULEB128 (u32 , reader )}),
784
+ else = > unreachable ,
785
+ }
786
+ const end_opcode = try std .leb .readULEB128 (u8 , reader );
787
+ if (end_opcode != std .wasm .opcode (.end )) {
788
+ std .debug .print ("expected 'end' opcode in init expression\n " , .{});
789
+ return error .MissingEndOpcode ;
790
+ }
791
+ }
792
+
793
+ fn parseDumpNames (reader : anytype , writer : anytype , data : []const u8 ) ! void {
794
+ while (reader .context .pos < data .len ) {
795
+ try parseDumpType (std .wasm .NameSubsection , reader , writer );
796
+ const size = try std .leb .readULEB128 (u32 , reader );
797
+ const entries = try std .leb .readULEB128 (u32 , reader );
798
+ try writer .print (
799
+ \\size {d}
800
+ \\names {d}
801
+ , .{ size , entries });
802
+ try writer .writeByte ('\n ' );
803
+ var i : u32 = 0 ;
804
+ while (i < entries ) : (i += 1 ) {
805
+ const index = try std .leb .readULEB128 (u32 , reader );
806
+ const name_len = try std .leb .readULEB128 (u32 , reader );
807
+ const pos = reader .context .pos ;
808
+ const name = data [pos .. ][0.. name_len ];
809
+ reader .context .pos += name_len ;
810
+
811
+ try writer .print (
812
+ \\index {d}
813
+ \\name {s}
814
+ , .{ index , name });
815
+ try writer .writeByte ('\n ' );
816
+ }
817
+ }
818
+ }
819
+ };
0 commit comments