metasm 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -0
  3. data.tar.gz.sig +0 -0
  4. data/Gemfile +3 -2
  5. data/metasm.gemspec +3 -2
  6. data/metasm.rb +4 -1
  7. data/metasm/compile_c.rb +2 -2
  8. data/metasm/cpu/arc/decode.rb +0 -21
  9. data/metasm/cpu/arc/main.rb +4 -4
  10. data/metasm/cpu/arm/decode.rb +1 -5
  11. data/metasm/cpu/arm/main.rb +3 -3
  12. data/metasm/cpu/arm64/decode.rb +2 -6
  13. data/metasm/cpu/arm64/main.rb +5 -5
  14. data/metasm/cpu/bpf/decode.rb +3 -35
  15. data/metasm/cpu/bpf/main.rb +5 -5
  16. data/metasm/cpu/bpf/render.rb +1 -12
  17. data/metasm/cpu/cy16/decode.rb +0 -6
  18. data/metasm/cpu/cy16/main.rb +3 -3
  19. data/metasm/cpu/cy16/render.rb +0 -11
  20. data/metasm/cpu/dalvik/decode.rb +4 -26
  21. data/metasm/cpu/dalvik/main.rb +20 -2
  22. data/metasm/cpu/dalvik/opcodes.rb +3 -2
  23. data/metasm/cpu/{mips/compile_c.rb → ebpf.rb} +5 -2
  24. data/metasm/cpu/ebpf/debug.rb +61 -0
  25. data/metasm/cpu/ebpf/decode.rb +142 -0
  26. data/metasm/cpu/ebpf/main.rb +58 -0
  27. data/metasm/cpu/ebpf/opcodes.rb +97 -0
  28. data/metasm/cpu/ebpf/render.rb +36 -0
  29. data/metasm/cpu/ia32/debug.rb +39 -1
  30. data/metasm/cpu/ia32/decode.rb +111 -90
  31. data/metasm/cpu/ia32/decompile.rb +45 -37
  32. data/metasm/cpu/ia32/main.rb +10 -0
  33. data/metasm/cpu/ia32/parse.rb +6 -0
  34. data/metasm/cpu/mcs51/decode.rb +1 -1
  35. data/metasm/cpu/mcs51/main.rb +11 -0
  36. data/metasm/cpu/mips/decode.rb +8 -18
  37. data/metasm/cpu/mips/main.rb +3 -3
  38. data/metasm/cpu/mips/opcodes.rb +1 -1
  39. data/metasm/cpu/msp430/decode.rb +2 -6
  40. data/metasm/cpu/msp430/main.rb +3 -3
  41. data/metasm/cpu/openrisc.rb +11 -0
  42. data/metasm/cpu/openrisc/debug.rb +106 -0
  43. data/metasm/cpu/openrisc/decode.rb +182 -0
  44. data/metasm/cpu/openrisc/decompile.rb +350 -0
  45. data/metasm/cpu/openrisc/main.rb +70 -0
  46. data/metasm/cpu/openrisc/opcodes.rb +109 -0
  47. data/metasm/cpu/openrisc/render.rb +37 -0
  48. data/metasm/cpu/ppc/decode.rb +0 -25
  49. data/metasm/cpu/ppc/main.rb +6 -6
  50. data/metasm/cpu/ppc/opcodes.rb +3 -4
  51. data/metasm/cpu/python/decode.rb +0 -20
  52. data/metasm/cpu/python/main.rb +1 -1
  53. data/metasm/cpu/sh4/decode.rb +2 -6
  54. data/metasm/cpu/sh4/main.rb +25 -23
  55. data/metasm/cpu/st20/decode.rb +0 -7
  56. data/metasm/cpu/webasm.rb +11 -0
  57. data/metasm/cpu/webasm/debug.rb +31 -0
  58. data/metasm/cpu/webasm/decode.rb +321 -0
  59. data/metasm/cpu/webasm/decompile.rb +386 -0
  60. data/metasm/cpu/webasm/encode.rb +104 -0
  61. data/metasm/cpu/webasm/main.rb +81 -0
  62. data/metasm/cpu/webasm/opcodes.rb +214 -0
  63. data/metasm/cpu/x86_64/compile_c.rb +13 -9
  64. data/metasm/cpu/x86_64/parse.rb +1 -1
  65. data/metasm/cpu/z80/decode.rb +0 -27
  66. data/metasm/cpu/z80/main.rb +3 -3
  67. data/metasm/cpu/z80/render.rb +0 -11
  68. data/metasm/debug.rb +43 -8
  69. data/metasm/decode.rb +62 -14
  70. data/metasm/decompile.rb +793 -466
  71. data/metasm/disassemble.rb +188 -131
  72. data/metasm/disassemble_api.rb +30 -17
  73. data/metasm/dynldr.rb +2 -2
  74. data/metasm/encode.rb +8 -2
  75. data/metasm/exe_format/autoexe.rb +2 -0
  76. data/metasm/exe_format/coff.rb +21 -3
  77. data/metasm/exe_format/coff_decode.rb +12 -0
  78. data/metasm/exe_format/coff_encode.rb +6 -3
  79. data/metasm/exe_format/dex.rb +13 -3
  80. data/metasm/exe_format/elf.rb +12 -2
  81. data/metasm/exe_format/elf_decode.rb +59 -1
  82. data/metasm/exe_format/main.rb +2 -0
  83. data/metasm/exe_format/mz.rb +1 -0
  84. data/metasm/exe_format/pe.rb +25 -3
  85. data/metasm/exe_format/wasm.rb +402 -0
  86. data/metasm/gui/dasm_decomp.rb +171 -95
  87. data/metasm/gui/dasm_graph.rb +61 -2
  88. data/metasm/gui/dasm_hex.rb +2 -2
  89. data/metasm/gui/dasm_main.rb +45 -19
  90. data/metasm/gui/debug.rb +13 -4
  91. data/metasm/gui/gtk.rb +12 -4
  92. data/metasm/main.rb +108 -103
  93. data/metasm/os/emulator.rb +175 -0
  94. data/metasm/os/main.rb +11 -6
  95. data/metasm/parse.rb +23 -12
  96. data/metasm/parse_c.rb +189 -135
  97. data/metasm/preprocessor.rb +16 -1
  98. data/misc/openrisc-parser.rb +79 -0
  99. data/samples/dasm-plugins/scanxrefs.rb +6 -4
  100. data/samples/dasm-plugins/selfmodify.rb +8 -8
  101. data/samples/dbg-plugins/trace_func.rb +1 -1
  102. data/samples/disassemble-gui.rb +14 -3
  103. data/samples/emubios.rb +251 -0
  104. data/samples/emudbg.rb +127 -0
  105. data/samples/lindebug.rb +79 -78
  106. data/samples/metasm-shell.rb +8 -8
  107. data/tests/all.rb +1 -1
  108. data/tests/expression.rb +2 -0
  109. data/tests/graph_layout.rb +1 -1
  110. data/tests/ia32.rb +1 -0
  111. data/tests/mips.rb +1 -1
  112. data/tests/preprocessor.rb +18 -0
  113. metadata +124 -6
  114. metadata.gz.sig +0 -0
@@ -217,6 +217,8 @@ class ExeFormat
217
217
 
218
218
  def shortname; self.class.name.split('::').last.downcase; end
219
219
 
220
+ def inspect; "<Metasm::ExeFormat %s @%x>" % [shortname, object_id]; end
221
+
220
222
  module IntToHash
221
223
  # converts a constant name to its numeric value using the hash
222
224
  # {1 => 'toto', 2 => 'tata'}: 'toto' => 1, 42 => 42, 'tutu' => raise
@@ -11,6 +11,7 @@ require 'metasm/decode'
11
11
  module Metasm
12
12
  class MZ < ExeFormat
13
13
  MAGIC = 'MZ' # 0x4d5a
14
+ MAGIC.force_encoding('BINARY') if MAGIC.respond_to?(:force_encoding)
14
15
  class Header < SerialStruct
15
16
  mem :magic, 2, MAGIC
16
17
  words :cblp, :cp, :crlc, :cparhdr, :minalloc, :maxalloc, :ss, :sp, :csum, :ip, :cs, :lfarlc, :ovno
@@ -11,8 +11,9 @@ require 'metasm/exe_format/coff'
11
11
  module Metasm
12
12
  class PE < COFF
13
13
  MAGIC = "PE\0\0" # 0x50450000
14
+ MAGIC.force_encoding('BINARY') if MAGIC.respond_to?(:force_encoding)
14
15
 
15
- attr_accessor :coff_offset, :signature, :mz
16
+ attr_accessor :coff_offset, :signature, :mz, :productid
16
17
 
17
18
  def initialize(*a)
18
19
  super(*a)
@@ -26,7 +27,14 @@ class PE < COFF
26
27
  def decode_header
27
28
  @cursection ||= self
28
29
  @encoded.ptr = 0x3c
29
- @encoded.ptr = decode_word(@encoded)
30
+ lfanew = decode_word(@encoded)
31
+
32
+ try_rich_sz = [0x400, lfanew].min
33
+ rich_ary = []
34
+ rich_ary << decode_word(@encoded) while @encoded.ptr < try_rich_sz
35
+ @productid = decode_productid(rich_ary)
36
+
37
+ @encoded.ptr = lfanew
30
38
  @signature = @encoded.read(4)
31
39
  raise InvalidExeFormat, "Invalid PE signature #{@signature.inspect}" if @signature != MAGIC
32
40
  @coff_offset = @encoded.ptr
@@ -38,6 +46,20 @@ class PE < COFF
38
46
  super()
39
47
  end
40
48
 
49
+ RICH_MAGIC = 0x68636952 # "Rich"
50
+ def decode_productid(ary)
51
+ return unless idx = ary.index(RICH_MAGIC) and xorkey = ary[idx+1]
52
+ ary = ary[0, idx-1].map { |dw| dw ^ xorkey }
53
+ return unless idx = ary.rindex(0x536E6144) # "DanS"
54
+ ary = ary[idx+2..-1]
55
+ out = []
56
+ until ary.empty?
57
+ ar1 = ary.shift
58
+ out << { :id => ar1 >> 16, :ver => ar1 & 0xffff, :count => ary.shift.to_i }
59
+ end
60
+ out
61
+ end
62
+
41
63
  # creates a default MZ file to be used in the PE header
42
64
  # this one is specially crafted to fit in the 0x3c bytes before the signature
43
65
  def encode_default_mz_header
@@ -226,7 +248,7 @@ EOS
226
248
  a.each { |aa|
227
249
  next if aa == Expression::Unknown
228
250
  dasm.auto_label_at(aa, 'seh', 'loc', 'sub')
229
- dasm.addrs_todo << [aa]
251
+ dasm.addrs_todo << { :addr => aa }
230
252
  }
231
253
  super(dasm, di)
232
254
  else
@@ -0,0 +1,402 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+
7
+ require 'metasm/exe_format/main'
8
+ require 'metasm/encode'
9
+ require 'metasm/decode'
10
+
11
+
12
+ module Metasm
13
+ # WebAssembly
14
+ # leb integer encoding taken from dex.rb
15
+ class WasmFile < ExeFormat
16
+ MAGIC = "\0asm"
17
+ MAGIC.force_encoding('binary') if MAGIC.respond_to?(:force_encoding)
18
+
19
+ SECTION_NAME = { 1 => 'Type', 2 => 'Import', 3 => 'Function', 4 => 'Table',
20
+ 5 => 'Memory', 6 => 'Global', 7 => 'Export', 8 => 'Start',
21
+ 9 => 'Element', 10 => 'Code', 11 => 'Data' }
22
+
23
+ TYPE = { -1 => 'i32', -2 => 'i64', -3 => 'f32', -4 => 'f64',
24
+ -0x10 => 'anyfunc', -0x20 => 'func', -0x40 => 'none' }
25
+
26
+ EXTERNAL_KIND = { 0 => 'function', 1 => 'table', 2 => 'memory', 3 => 'global' }
27
+
28
+ # begin WTF
29
+ OPCODE_IMM_COUNT = Hash.new(0)
30
+ [2, 3, 4, 0xc, 0xd, 0x10].each { |op| OPCODE_IMM_COUNT[op] = 1 }
31
+ OPCODE_IMM_COUNT[0x11] = 2
32
+ (0x20..0x24).each { |op| OPCODE_IMM_COUNT[op] = 1 }
33
+ (0x28..0x3e).each { |op| OPCODE_IMM_COUNT[op] = 2 }
34
+ (0x3f..0x42).each { |op| OPCODE_IMM_COUNT[op] = 1 }
35
+ # 0x43 followed by uint32, 0x44 followed by uint64 (float constants)
36
+ # end WTF
37
+
38
+
39
+ class SerialStruct < Metasm::SerialStruct
40
+ # TODO move uleb/sleb to new_field for sizeof
41
+ new_int_field :u4, :uleb, :sleb
42
+ end
43
+
44
+ class Header < SerialStruct
45
+ mem :sig, 4, MAGIC
46
+ decode_hook { |exe, hdr| raise InvalidExeFormat, "E: invalid WasmFile signature #{hdr.sig.inspect}" if hdr.sig != MAGIC }
47
+ u4 :ver, 1
48
+ end
49
+
50
+ class Module < SerialStruct
51
+ uleb :id
52
+ fld_enum :id, SECTION_NAME
53
+ uleb :payload_len
54
+ attr_accessor :edata, :raw_offset, :name
55
+
56
+ def decode(exe)
57
+ super(exe)
58
+ @raw_offset = exe.encoded.ptr
59
+ @edata = exe.encoded[exe.encoded.ptr, @payload_len]
60
+ exe.encoded.ptr += @payload_len
61
+ end
62
+ end
63
+
64
+ attr_accessor :endianness
65
+
66
+ def encode_u4(val) Expression[val].encode(:u32, @endianness) end
67
+ def decode_u4(edata = @encoded) edata.decode_imm(:u32, @endianness) end
68
+ def sizeof_u4 ; 4 ; end
69
+ def encode_uleb(val, signed=false)
70
+ v = val
71
+ out = EncodedData.new
72
+ while v > 0x7f or v < -0x40 or (signed and v > 0x3f)
73
+ out << Expression[0x80 | (v&0x7f)].encode(:u8, @endianness)
74
+ v >>= 7
75
+ end
76
+ out << Expression[v & 0x7f].encode(:u8, @endianness)
77
+ end
78
+ def decode_uleb(ed = @encoded, signed=false)
79
+ v = s = 0
80
+ while s < 10*7
81
+ b = ed.read(1).unpack('C').first.to_i
82
+ v |= (b & 0x7f) << s
83
+ s += 7
84
+ break if (b&0x80) == 0
85
+ end
86
+ v = Expression.make_signed(v, s) if signed
87
+ v
88
+ end
89
+ def encode_sleb(val) encode_uleb(val, true) end
90
+ def decode_sleb(ed = @encoded) decode_uleb(ed, true) end
91
+ attr_accessor :header, :modules, :type, :import, :function_signature,
92
+ :table, :memory, :global, :export, :start_function_index,
93
+ :element, :function_body, :data, :code_info
94
+
95
+ def initialize(endianness=:little)
96
+ @endianness = endianness
97
+ @encoded = EncodedData.new
98
+ super()
99
+ end
100
+
101
+ def decode_type(edata=@encoded)
102
+ form = decode_sleb(edata)
103
+ type = TYPE[form] || "unk_type_#{form}"
104
+ if type == 'func'
105
+ type = { :params => [], :ret => [] }
106
+ decode_uleb(edata).times {
107
+ type[:params] << decode_type(edata)
108
+ }
109
+ decode_uleb(edata).times {
110
+ type[:ret] << decode_type(edata)
111
+ }
112
+ end
113
+ type
114
+ end
115
+
116
+ # return the nth global
117
+ # use the @global array and the @import array
118
+ def get_global_nr(nr)
119
+ glob_imports = @import.to_a.find_all { |i| i[:kind] == 'global' }
120
+ return glob_imports[nr] if nr < glob_imports.length
121
+ nr -= glob_imports.length
122
+ @global[nr]
123
+ end
124
+
125
+ # return the nth function body
126
+ # use the @function_body array and the @import array
127
+ def get_function_nr(nr)
128
+ func_imports = @import.to_a.find_all { |i| i[:kind] == 'function' }
129
+ return func_imports[nr] if nr < func_imports.length
130
+ nr -= func_imports.length
131
+ @function_body[nr]
132
+ end
133
+
134
+ def type_to_s(t)
135
+ return t unless t.kind_of?(::Hash)
136
+ (t[:ret].map { |tt| type_to_s(tt) }.join(', ') << ' f(' << t[:params].map { |tt| type_to_s(tt) }.join(', ') << ')').strip
137
+ end
138
+
139
+ def decode_limits(edata=@encoded)
140
+ flags = decode_uleb(edata)
141
+ out = { :initial_size => decode_uleb(edata) }
142
+ out[:maximum] = decode_uleb(edata) if flags & 1
143
+ out
144
+ end
145
+
146
+ # wtf
147
+ # read wasm bytecode until reaching the end opcode
148
+ # return the byte offset
149
+ def read_code_until_end(m=nil)
150
+ if m
151
+ raw_offset = m.raw_offset + m.edata.ptr
152
+ edata = m.edata
153
+ else
154
+ edata = @encoded
155
+ end
156
+
157
+ while op = edata.decode_imm(:u8, @endianness)
158
+ case op
159
+ when 0xb
160
+ # end opcode
161
+ return raw_offset
162
+ when 0xe
163
+ # indirect branch wtf
164
+ decode_uleb(edata).times { decode_uleb(edata) }
165
+ decode_uleb(edata)
166
+ when 0x43
167
+ edata.read(4)
168
+ when 0x44
169
+ edata.read(8)
170
+ else
171
+ OPCODE_IMM_COUNT[op].times { decode_uleb(edata) }
172
+ end
173
+ end
174
+ raw_offset
175
+ end
176
+
177
+ def decode_header
178
+ @header = Header.decode(self)
179
+ @modules = []
180
+ end
181
+
182
+ def decode
183
+ decode_header
184
+ while @encoded.ptr < @encoded.length
185
+ @modules << Module.decode(self)
186
+ end
187
+ @modules.each { |m|
188
+ @encoded.add_export(new_label("module_#{m.id}"), m.raw_offset)
189
+ f = "decode_module_#{m.id.to_s.downcase}"
190
+ send(f, m) if respond_to?(f)
191
+ }
192
+ func_imports = @import.to_a.find_all { |i| i[:kind] == 'function' }
193
+ export.to_a.each { |e|
194
+ next if e[:kind] != 'function' # TODO resolve init_offset for globals etc?
195
+ idx = e[:index] - func_imports.length
196
+ next if not fb = function_body.to_a[idx]
197
+ @encoded.add_export(new_label(e[:field]), fb[:init_offset], true)
198
+ }
199
+ # bytecode start addr => { :local_var => [], :params => [], :ret => [] }
200
+ # :local_var absent for external code (imported funcs)
201
+ @code_info = {}
202
+ import.to_a.each { |i|
203
+ next unless i[:kind] == 'function'
204
+ @code_info["#{i[:module]}_#{i[:field]}"] = { :params => i[:type][:params], :ret => i[:type][:ret] }
205
+ }
206
+ function_body.to_a.each { |fb|
207
+ @code_info[fb[:init_offset]] = { :local_var => fb[:local_var], :params => fb[:type][:params], :ret => fb[:type][:ret] }
208
+ }
209
+ global_idx = import.to_a.find_all { |i| i[:kind] == 'global' }.length - 1
210
+ global.to_a.each { |g|
211
+ @code_info[g[:init_offset]] = { :local_var => [], :params => [], :ret => [g[:type]] }
212
+ @encoded.add_export new_label("global_#{global_idx += 1}_init"), g[:init_offset]
213
+ }
214
+ element.to_a.each { |e|
215
+ @code_info[e[:init_offset]] = { :local_var => [], :params => [], :ret => ['i32'] }
216
+ }
217
+ data.to_a.each { |d|
218
+ @code_info[d[:init_offset]] = { :local_var => [], :params => [], :ret => ['i32'] }
219
+ }
220
+ end
221
+
222
+ def decode_module_type(m)
223
+ @type = []
224
+ decode_uleb(m.edata).times {
225
+ @type << decode_type(m.edata)
226
+ }
227
+ end
228
+
229
+ def decode_module_import(m)
230
+ @import = []
231
+ decode_uleb(m.edata).times {
232
+ i = {}
233
+ i[:module] = m.edata.read(decode_uleb(m.edata))
234
+ i[:field] = m.edata.read(decode_uleb(m.edata))
235
+ kind = decode_uleb(m.edata)
236
+ i[:kind] = EXTERNAL_KIND[kind] || kind
237
+ case i[:kind]
238
+ when 'function'
239
+ i[:type] = @type[decode_uleb(m.edata)] # XXX keep index only, in case @type is not yet known ?
240
+ when 'table'
241
+ i[:type] = decode_type(m.edata)
242
+ i[:limits] = decode_limits(m.edata)
243
+ when 'memory'
244
+ i[:limits] = decode_limits(m.edata)
245
+ when 'global'
246
+ i[:type] = decode_type(m.edata)
247
+ i[:mutable] = decode_uleb(m.edata)
248
+ end
249
+ @import << i
250
+ }
251
+ end
252
+
253
+ def decode_module_function(m)
254
+ @function_signature = []
255
+ idx = 0
256
+ decode_uleb(m.edata).times {
257
+ @function_signature << @type[decode_uleb(m.edata)]
258
+ @function_body[idx][:type] = @function_signature[idx] if function_body
259
+ idx += 1
260
+ }
261
+ end
262
+
263
+ def decode_module_table(m)
264
+ @table = []
265
+ decode_uleb(m.edata).times {
266
+ @table << { :type => decode_type(m.edata), :limits => decode_limits(m.edata) }
267
+ }
268
+ end
269
+
270
+ def decode_module_memory(m)
271
+ @memory = []
272
+ decode_uleb(m.edata).times {
273
+ @memory << { :limits => decode_limits(m.edata) }
274
+ }
275
+ end
276
+
277
+ def decode_module_global(m)
278
+ @global = []
279
+ decode_uleb(m.edata).times {
280
+ @global << { :type => decode_type(m.edata), :mutable => decode_uleb(m.edata), :init_offset => read_code_until_end(m) }
281
+ }
282
+ end
283
+
284
+ def decode_module_export(m)
285
+ @export = []
286
+ decode_uleb(m.edata).times {
287
+ flen = decode_uleb(m.edata)
288
+ fld = m.edata.read(flen)
289
+ kind = decode_uleb(m.edata)
290
+ kind = EXTERNAL_KIND[kind] || kind
291
+ index = decode_uleb(m.edata)
292
+ @export << { :field => fld, :kind => kind, :index => index }
293
+ }
294
+ end
295
+
296
+ def decode_module_start(m)
297
+ @start_function_index = decode_uleb(m.edata)
298
+ end
299
+
300
+ def decode_module_element(m)
301
+ @element = []
302
+ decode_uleb(m.edata).times {
303
+ seg = { :table_index => decode_uleb(m.edata),
304
+ :init_offset => read_code_until_end(m),
305
+ :elems => [] }
306
+ decode_uleb(m.edata).times {
307
+ seg[:elems] << decode_uleb(m.edata)
308
+ }
309
+ @element << seg
310
+ @encoded.add_export new_label("element_#{@element.length-1}_init_addr"), @element.last[:init_offset]
311
+ }
312
+ end
313
+
314
+ def decode_module_code(m)
315
+ @function_body = []
316
+ idx = 0
317
+ decode_uleb(m.edata).times {
318
+ local_vars = []
319
+ body_size = decode_uleb(m.edata) # size of local defs + bytecode (in bytes)
320
+ next_ptr = m.edata.ptr + body_size
321
+ decode_uleb(m.edata).times { # nr of local vars types
322
+ n_vars_of_this_type = decode_uleb(m.edata) # nr of local vars of this type
323
+ type = decode_type(m.edata) # actual type
324
+ n_vars_of_this_type.times {
325
+ local_vars << type
326
+ }
327
+ }
328
+ code_offset = m.raw_offset + m.edata.ptr # bytecode comes next
329
+ m.edata.ptr = next_ptr
330
+ @function_body << { :local_var => local_vars, :init_offset => code_offset }
331
+ @function_body.last[:type] = @function_signature[idx] if function_signature
332
+ @encoded.add_export new_label("function_#{@function_body.length-1}"), @function_body.last[:init_offset]
333
+ idx += 1
334
+ }
335
+ end
336
+
337
+ def decode_module_data(m)
338
+ @data = []
339
+ decode_uleb(m.edata).times {
340
+ idx = decode_uleb(m.edata)
341
+ initoff = read_code_until_end(m)
342
+ data_len = decode_uleb(m.edata)
343
+ data_start_ptr = m.raw_offset + m.edata.ptr
344
+ data = m.edata.read(data_len)
345
+ data_end_ptr = m.raw_offset + m.edata.ptr
346
+
347
+ @data << { :index => idx, :init_offset => initoff, :data => data }
348
+ @encoded.add_export new_label("data_#{@data.length-1}_init_addr"), initoff
349
+ @encoded.add_export new_label("data_#{@data.length-1}_start"), data_start_ptr
350
+ @encoded.add_export new_label("data_#{@data.length-1}_end"), data_end_ptr
351
+ }
352
+ end
353
+
354
+ def decode_module_0(m)
355
+ # id == 0 for not well-known modules
356
+ # the module name is encoded at start of payload (uleb name length + actual name)
357
+ m.name = m.edata.read(decode_uleb(m.edata))
358
+ f = "decode_module_0_#{m.name.downcase}"
359
+ send(f, m) if respond_to?(f)
360
+ end
361
+
362
+ def decode_module_0_name(m)
363
+ # TODO parse stored names of local variables etc
364
+ end
365
+
366
+ def cpu_from_headers
367
+ WebAsm.new(self)
368
+ end
369
+
370
+ def init_disassembler
371
+ dasm = super()
372
+ function_body.to_a.each { |fb|
373
+ v = []
374
+ fb[:local_var].map { |lv| type_to_s(lv) }.each { |lv|
375
+ v.last && lv == v.last.last ? v.last << lv : v << [lv]
376
+ }
377
+ v.map! { |sublist|
378
+ # i32 ; i32 ; i32 ; i32 ; i32 ; i32 ; i64 -> 5 * i32 ; i64
379
+ sublist.length > 3 ? "#{sublist.length} * #{sublist.first}" : sublist.join(' ; ')
380
+ }
381
+ dasm.add_comment fb[:init_offset], "proto: #{fb[:type] ? type_to_s(fb[:type]) : 'unknown'}"
382
+ dasm.add_comment fb[:init_offset], "vars: #{v.join(' ; ')}"
383
+ }
384
+ global.to_a.each { |g|
385
+ dasm.add_comment g[:init_offset], "type: #{type_to_s(g[:type])}"
386
+ }
387
+ dasm.function[:default] = @cpu.disassembler_default_func
388
+ dasm
389
+ end
390
+
391
+ def each_section
392
+ yield @encoded, 0
393
+ end
394
+
395
+ def get_default_entrypoints
396
+ global.to_a.map { |g| g[:init_offset] } +
397
+ element.to_a.map { |e| e[:init_offset] } +
398
+ data.to_a.map { |d| d[:init_offset] } +
399
+ function_body.to_a.map { |f| f[:init_offset] }
400
+ end
401
+ end
402
+ end