metasm 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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