unmangler 0.0.1 → 0.0.2
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.
- checksums.yaml +8 -8
- data/.gitignore +4 -0
- data/lib/unmangler.rb +6 -4
- data/lib/unmangler/base.rb +27 -0
- data/lib/unmangler/borland.rb +109 -128
- data/lib/unmangler/msvc.rb +1025 -0
- data/lib/unmangler/string_ptr.rb +26 -1
- data/lib/unmangler/version.rb +1 -1
- data/spec/borland_samples_spec.rb +28 -0
- data/spec/borland_spec.rb +2 -0
- data/spec/msvc_samples_spec.rb +28 -0
- data/spec/msvc_spec.rb +61 -0
- data/spec/samples_spec.rb +51 -0
- data/spec/spec_helper.rb +31 -6
- data/unmangler.gemspec +3 -1
- metadata +12 -2
@@ -0,0 +1,1025 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require File.expand_path("base", File.dirname(__FILE__))
|
3
|
+
|
4
|
+
# ported from wine's undname.c
|
5
|
+
#
|
6
|
+
# most of the comments are from undname.c
|
7
|
+
|
8
|
+
module Unmangler; end
|
9
|
+
|
10
|
+
class Unmangler::MSVC < Unmangler::Base
|
11
|
+
|
12
|
+
UNDNAME_COMPLETE = 0x0000
|
13
|
+
UNDNAME_NO_LEADING_UNDERSCORES = 0x0001 # Don't show __ in calling convention
|
14
|
+
UNDNAME_NO_MS_KEYWORDS = 0x0002 # Don't show calling convention at all
|
15
|
+
UNDNAME_NO_FUNCTION_RETURNS = 0x0004 # Don't show function/method return value
|
16
|
+
UNDNAME_NO_ALLOCATION_MODEL = 0x0008
|
17
|
+
UNDNAME_NO_ALLOCATION_LANGUAGE = 0x0010
|
18
|
+
UNDNAME_NO_MS_THISTYPE = 0x0020
|
19
|
+
UNDNAME_NO_CV_THISTYPE = 0x0040
|
20
|
+
UNDNAME_NO_THISTYPE = 0x0060
|
21
|
+
UNDNAME_NO_ACCESS_SPECIFIERS = 0x0080 # Don't show access specifier = public/protected/private
|
22
|
+
UNDNAME_NO_THROW_SIGNATURES = 0x0100
|
23
|
+
UNDNAME_NO_MEMBER_TYPE = 0x0200 # Don't show static/virtual specifier
|
24
|
+
UNDNAME_NO_RETURN_UDT_MODEL = 0x0400
|
25
|
+
UNDNAME_32_BIT_DECODE = 0x0800
|
26
|
+
UNDNAME_NAME_ONLY = 0x1000 # Only report the variable/method name
|
27
|
+
UNDNAME_NO_ARGUMENTS = 0x2000 # Don't show method arguments
|
28
|
+
UNDNAME_NO_SPECIAL_SYMS = 0x4000
|
29
|
+
UNDNAME_NO_COMPLEX_TYPE = 0x8000
|
30
|
+
|
31
|
+
class ParsedSymbol < Struct.new(
|
32
|
+
:flags, # (unsigned int) the UNDNAME_ flags used for demangling
|
33
|
+
:current, # (const char*) pointer in input (mangled) string
|
34
|
+
:result, # (char*) demangled string
|
35
|
+
:names, # (struct array) array of names for back reference
|
36
|
+
:stack, # (struct array) stack of parsed strings
|
37
|
+
:alloc_list, # (void*) linked list of allocated blocks
|
38
|
+
:avail_in_first # (usigned int) number of available bytes in head block
|
39
|
+
)
|
40
|
+
def initialize *args
|
41
|
+
super
|
42
|
+
self.names ||= []
|
43
|
+
self.stack ||= []
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
DataType = Struct.new :left, :right # char*
|
48
|
+
|
49
|
+
def unmangle mangled, flags = UNDNAME_COMPLETE
|
50
|
+
|
51
|
+
if flags.is_a?(Hash)
|
52
|
+
flags = flags.fetch(:args, true) ? UNDNAME_COMPLETE : UNDNAME_NAME_ONLY
|
53
|
+
end
|
54
|
+
|
55
|
+
if flags & UNDNAME_NAME_ONLY != 0
|
56
|
+
flags |= UNDNAME_NO_FUNCTION_RETURNS | UNDNAME_NO_ACCESS_SPECIFIERS |
|
57
|
+
UNDNAME_NO_MEMBER_TYPE | UNDNAME_NO_ALLOCATION_LANGUAGE | UNDNAME_NO_COMPLEX_TYPE
|
58
|
+
end
|
59
|
+
|
60
|
+
sym = ParsedSymbol.new
|
61
|
+
sym.flags = flags
|
62
|
+
sym.current = StringPtr.new(mangled)
|
63
|
+
|
64
|
+
symbol_demangle(sym) ? sym.result.strip : mangled
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# def warn fmt, *args
|
70
|
+
# STDERR.printf(fmt, *args)
|
71
|
+
# end
|
72
|
+
|
73
|
+
def err fmt, *args
|
74
|
+
if @debug
|
75
|
+
begin
|
76
|
+
STDERR.printf(fmt, *args)
|
77
|
+
rescue
|
78
|
+
STDERR.puts "[!] #{fmt.strip.inspect}, #{args.inspect}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
alias :warn :err
|
84
|
+
|
85
|
+
def symbol_demangle sym
|
86
|
+
ret = false
|
87
|
+
do_after = 0
|
88
|
+
dashed_null = "--null--"
|
89
|
+
|
90
|
+
#printf "[d] in: %s\n", sym.current[0..-1]
|
91
|
+
|
92
|
+
catch(:done) do
|
93
|
+
# seems wrong as name, as it demangles a simple data type
|
94
|
+
if sym.flags & UNDNAME_NO_ARGUMENTS != 0
|
95
|
+
ct = DataType.new
|
96
|
+
if (demangle_datatype(sym, ct, nil, false))
|
97
|
+
sym.result = sprintf("%s%s", ct.left, ct.right);
|
98
|
+
ret = true
|
99
|
+
end
|
100
|
+
throw :done
|
101
|
+
end # if
|
102
|
+
|
103
|
+
# MS mangled names always begin with '?'
|
104
|
+
return false unless sym.current[0] == '?'
|
105
|
+
sym.current.inc!
|
106
|
+
|
107
|
+
# Then function name or operator code
|
108
|
+
if (sym.current[0] == '?' && (sym.current[1] != '$' || sym.current[2] == '?'))
|
109
|
+
function_name = nil
|
110
|
+
|
111
|
+
if (sym.current[1] == '$')
|
112
|
+
do_after = 6
|
113
|
+
sym.current += 2
|
114
|
+
end
|
115
|
+
|
116
|
+
# C++ operator code (one character, or two if the first is '_')
|
117
|
+
case sym.current.inc_get!
|
118
|
+
when '0'; do_after = 1
|
119
|
+
when '1'; do_after = 2
|
120
|
+
when '2'; function_name = "operator new"
|
121
|
+
when '3'; function_name = "operator delete"
|
122
|
+
when '4'; function_name = "operator="
|
123
|
+
when '5'; function_name = "operator>>"
|
124
|
+
when '6'; function_name = "operator<<"
|
125
|
+
when '7'; function_name = "operator!"
|
126
|
+
when '8'; function_name = "operator=="
|
127
|
+
when '9'; function_name = "operator!="
|
128
|
+
when 'A'; function_name = "operator[]"
|
129
|
+
when 'B'; function_name = "operator "; do_after = 3
|
130
|
+
when 'C'; function_name = "operator."
|
131
|
+
when 'D'; function_name = "operator*"
|
132
|
+
when 'E'; function_name = "operator++"
|
133
|
+
when 'F'; function_name = "operator--"
|
134
|
+
when 'G'; function_name = "operator-"
|
135
|
+
when 'H'; function_name = "operator+"
|
136
|
+
when 'I'; function_name = "operator&"
|
137
|
+
when 'J'; function_name = "operator.*"
|
138
|
+
when 'K'; function_name = "operator/"
|
139
|
+
when 'L'; function_name = "operator%"
|
140
|
+
when 'M'; function_name = "operator<"
|
141
|
+
when 'N'; function_name = "operator<="
|
142
|
+
when 'O'; function_name = "operator>"
|
143
|
+
when 'P'; function_name = "operator>="
|
144
|
+
when 'Q'; function_name = "operator,"
|
145
|
+
when 'R'; function_name = "operator()"
|
146
|
+
when 'S'; function_name = "operator~"
|
147
|
+
when 'T'; function_name = "operator^"
|
148
|
+
when 'U'; function_name = "operator|"
|
149
|
+
when 'V'; function_name = "operator&&"
|
150
|
+
when 'W'; function_name = "operator||"
|
151
|
+
when 'X'; function_name = "operator*="
|
152
|
+
when 'Y'; function_name = "operator+="
|
153
|
+
when 'Z'; function_name = "operator-="
|
154
|
+
when '_'
|
155
|
+
case sym.current.inc_get!
|
156
|
+
when '0'; function_name = "operator/="
|
157
|
+
when '1'; function_name = "operator%="
|
158
|
+
when '2'; function_name = "operator>>="
|
159
|
+
when '3'; function_name = "operator<<="
|
160
|
+
when '4'; function_name = "operator&="
|
161
|
+
when '5'; function_name = "operator|="
|
162
|
+
when '6'; function_name = "operator^="
|
163
|
+
when '7'; function_name = "`vftable'"
|
164
|
+
when '8'; function_name = "`vbtable'"
|
165
|
+
when '9'; function_name = "`vcall'"
|
166
|
+
when 'A'; function_name = "`typeof'"
|
167
|
+
when 'B'; function_name = "`local static guard'"
|
168
|
+
when 'C'; function_name = "`string'"; do_after = 4
|
169
|
+
when 'D'; function_name = "`vbase destructor'"
|
170
|
+
when 'E'; function_name = "`vector deleting destructor'"
|
171
|
+
when 'F'; function_name = "`default constructor closure'"
|
172
|
+
when 'G'; function_name = "`scalar deleting destructor'"
|
173
|
+
when 'H'; function_name = "`vector constructor iterator'"
|
174
|
+
when 'I'; function_name = "`vector destructor iterator'"
|
175
|
+
when 'J'; function_name = "`vector vbase constructor iterator'"
|
176
|
+
when 'K'; function_name = "`virtual displacement map'"
|
177
|
+
when 'L'; function_name = "`eh vector constructor iterator'"
|
178
|
+
when 'M'; function_name = "`eh vector destructor iterator'"
|
179
|
+
when 'N'; function_name = "`eh vector vbase constructor iterator'"
|
180
|
+
when 'O'; function_name = "`copy constructor closure'"
|
181
|
+
when 'R'
|
182
|
+
sym.flags |= UNDNAME_NO_FUNCTION_RETURNS
|
183
|
+
case sym.current.inc_get!
|
184
|
+
when '0'
|
185
|
+
ct = DataType.new
|
186
|
+
pmt = []
|
187
|
+
sym.current.inc!
|
188
|
+
demangle_datatype(sym, ct, pmt, false)
|
189
|
+
function_name = sprintf("%s%s `RTTI Type Descriptor'", ct.left, ct.right)
|
190
|
+
sym.current.dec!
|
191
|
+
when '1'
|
192
|
+
sym.current.inc!
|
193
|
+
n1 = get_number(sym)
|
194
|
+
n2 = get_number(sym)
|
195
|
+
n3 = get_number(sym)
|
196
|
+
n4 = get_number(sym)
|
197
|
+
sym.current.dec!
|
198
|
+
function_name = sprintf("`RTTI Base Class Descriptor at (%s,%s,%s,%s)'", n1, n2, n3, n4)
|
199
|
+
when '2'; function_name = "`RTTI Base Class Array'"
|
200
|
+
when '3'; function_name = "`RTTI Class Hierarchy Descriptor'"
|
201
|
+
when '4'; function_name = "`RTTI Complete Object Locator'"
|
202
|
+
else
|
203
|
+
err("Unknown RTTI operator: _R%c\n", sym.current[0])
|
204
|
+
end # case sym.current.inc_get!
|
205
|
+
when 'S'; function_name = "`local vftable'"
|
206
|
+
when 'T'; function_name = "`local vftable constructor closure'"
|
207
|
+
when 'U'; function_name = "operator new[]"
|
208
|
+
when 'V'; function_name = "operator delete[]"
|
209
|
+
when 'X'; function_name = "`placement delete closure'"
|
210
|
+
when 'Y'; function_name = "`placement delete[] closure'"
|
211
|
+
else
|
212
|
+
err("Unknown operator: _%c\n", sym.current[0])
|
213
|
+
return false
|
214
|
+
end # case sym.current.inc_get!
|
215
|
+
else
|
216
|
+
# FIXME: Other operators
|
217
|
+
err("Unknown operator: %c\n", sym.current[0])
|
218
|
+
return false
|
219
|
+
end # case sym.current.inc_get!
|
220
|
+
|
221
|
+
sym.current.inc!
|
222
|
+
|
223
|
+
case do_after
|
224
|
+
when 1,2
|
225
|
+
sym.stack << dashed_null
|
226
|
+
when 4
|
227
|
+
sym.result = function_name
|
228
|
+
ret = true
|
229
|
+
throw :done
|
230
|
+
else
|
231
|
+
if do_after == 6
|
232
|
+
array_pmt = []
|
233
|
+
if args = get_args(sym, array_pmt, false, '<', '>')
|
234
|
+
function_name << args
|
235
|
+
end
|
236
|
+
sym.names = []
|
237
|
+
end
|
238
|
+
sym.stack << function_name
|
239
|
+
end # case do_after
|
240
|
+
|
241
|
+
elsif sym.current[0] == '$'
|
242
|
+
# Strange construct, it's a name with a template argument list and that's all.
|
243
|
+
sym.current.inc!
|
244
|
+
ret = (sym.result = get_template_name(sym)) != nil
|
245
|
+
throw :done
|
246
|
+
elsif sym.current[0,2] == '?$'
|
247
|
+
do_after = 5
|
248
|
+
end
|
249
|
+
|
250
|
+
# Either a class name, or '@' if the symbol is not a class member
|
251
|
+
case sym.current[0]
|
252
|
+
when '@'; sym.current.inc!
|
253
|
+
when '$'; # do nothing
|
254
|
+
else
|
255
|
+
# Class the function is associated with, terminated by '@@'
|
256
|
+
throw :done unless get_class(sym)
|
257
|
+
end # case sym.current[0]
|
258
|
+
|
259
|
+
case do_after
|
260
|
+
when 1,2
|
261
|
+
# it's time to set the member name for ctor & dtor
|
262
|
+
throw :done if sym.stack.size <= 1 # ZZZ may be wrong
|
263
|
+
if do_after == 1
|
264
|
+
sym.stack[0] = sym.stack[1]
|
265
|
+
else
|
266
|
+
sym.stack[0] = "~" + sym.stack[1]
|
267
|
+
end
|
268
|
+
# ctors and dtors don't have return type
|
269
|
+
sym.flags |= UNDNAME_NO_FUNCTION_RETURNS
|
270
|
+
when 3
|
271
|
+
sym.flags &= ~UNDNAME_NO_FUNCTION_RETURNS
|
272
|
+
when 5
|
273
|
+
sym.names.shift
|
274
|
+
end # case do_after
|
275
|
+
|
276
|
+
# Function/Data type and access level
|
277
|
+
ret =
|
278
|
+
case sym.current[0]
|
279
|
+
when /\d/
|
280
|
+
handle_data(sym)
|
281
|
+
when /[A-Z]/
|
282
|
+
handle_method(sym, do_after == 3)
|
283
|
+
when '$'
|
284
|
+
handle_template(sym)
|
285
|
+
else
|
286
|
+
false
|
287
|
+
end
|
288
|
+
end # catch(:done)
|
289
|
+
|
290
|
+
if ret
|
291
|
+
assert(sym.result)
|
292
|
+
else
|
293
|
+
warn("Failed at %s\n", sym.current[0..-1])
|
294
|
+
end
|
295
|
+
|
296
|
+
#printf "[d] out: %s\n", sym.result
|
297
|
+
|
298
|
+
ret
|
299
|
+
end
|
300
|
+
|
301
|
+
# Attempt to demangle a C++ data type, which may be datatype.
|
302
|
+
# a datatype type is made up of a number of simple types. e.g:
|
303
|
+
# char** = (pointer to (pointer to (char)))
|
304
|
+
|
305
|
+
def demangle_datatype(sym, ct, pmt_ref=nil, in_args=false)
|
306
|
+
dt = nil
|
307
|
+
add_pmt = true
|
308
|
+
|
309
|
+
assert(ct)
|
310
|
+
ct.left = ct.right = nil
|
311
|
+
|
312
|
+
catch :done do
|
313
|
+
case (dt = sym.current.get_inc!) #.tap{ |x| puts "[d] #{x}" }
|
314
|
+
when '_'
|
315
|
+
# MS type: __int8,__int16 etc
|
316
|
+
ct.left = get_extended_type(sym.current.get_inc!)
|
317
|
+
|
318
|
+
when *SIMPLE_TYPES.keys
|
319
|
+
# Simple data types
|
320
|
+
ct.left = get_simple_type(dt)
|
321
|
+
add_pmt = false
|
322
|
+
|
323
|
+
when 'T','U','V','Y'
|
324
|
+
# union, struct, class, cointerface
|
325
|
+
struct_name = type_name = nil
|
326
|
+
|
327
|
+
throw :done unless struct_name = get_class_name(sym)
|
328
|
+
|
329
|
+
if (sym.flags & UNDNAME_NO_COMPLEX_TYPE == 0)
|
330
|
+
case (dt)
|
331
|
+
when 'T'; type_name = "union "
|
332
|
+
when 'U'; type_name = "struct "
|
333
|
+
when 'V'; type_name = "class "
|
334
|
+
when 'Y'; type_name = "cointerface "
|
335
|
+
end
|
336
|
+
end
|
337
|
+
ct.left = sprintf("%s%s", type_name, struct_name)
|
338
|
+
|
339
|
+
when '?'
|
340
|
+
# not all the time is seems
|
341
|
+
if in_args
|
342
|
+
throw :done unless ptr = get_number(sym)
|
343
|
+
ct.left = "`template-parameter-#{ptr}'"
|
344
|
+
else
|
345
|
+
throw :done unless get_modified_type(ct, sym, pmt_ref, '?', in_args)
|
346
|
+
end
|
347
|
+
|
348
|
+
when 'A','B' # reference, volatile reference
|
349
|
+
throw :done unless get_modified_type(ct, sym, pmt_ref, dt, in_args)
|
350
|
+
|
351
|
+
when 'Q','R','S' # const pointer, volatile pointer, const volatile pointer
|
352
|
+
throw :done unless get_modified_type(ct, sym, pmt_ref, in_args ? dt : 'P', in_args)
|
353
|
+
|
354
|
+
when 'P' # Pointer
|
355
|
+
if isdigit(sym.current[0])
|
356
|
+
# FIXME: P6 = Function pointer, others who knows..
|
357
|
+
if (sym.current.get_inc! == '6')
|
358
|
+
call_conv = StringPtr.new
|
359
|
+
exported = StringPtr.new
|
360
|
+
sub_ct = DataType.new
|
361
|
+
saved_stack = sym.stack.dup
|
362
|
+
|
363
|
+
throw :done unless cc=get_calling_convention(
|
364
|
+
sym.current.get_inc!, sym.flags & ~UNDNAME_NO_ALLOCATION_LANGUAGE
|
365
|
+
)
|
366
|
+
call_conv, exported = cc
|
367
|
+
|
368
|
+
throw :done unless demangle_datatype(sym, sub_ct, pmt_ref, false)
|
369
|
+
throw :done unless args = get_args(sym, pmt_ref, true, '(', ')')
|
370
|
+
sym.stack = saved_stack
|
371
|
+
|
372
|
+
ct.left = sprintf("%s%s (%s*", sub_ct.left, sub_ct.right, call_conv)
|
373
|
+
ct.right = sprintf(")%s", args)
|
374
|
+
else
|
375
|
+
throw :done
|
376
|
+
end
|
377
|
+
else
|
378
|
+
throw :done unless get_modified_type(ct, sym, pmt_ref, 'P', in_args)
|
379
|
+
end
|
380
|
+
|
381
|
+
when 'W'
|
382
|
+
if (sym.current[0] == '4')
|
383
|
+
sym.current.inc!
|
384
|
+
throw :done unless enum_name = get_class_name(sym)
|
385
|
+
if sym.flags & UNDNAME_NO_COMPLEX_TYPE != 0
|
386
|
+
ct.left = enum_name
|
387
|
+
else
|
388
|
+
ct.left = sprintf("enum %s", enum_name)
|
389
|
+
end
|
390
|
+
else
|
391
|
+
throw :done
|
392
|
+
end
|
393
|
+
|
394
|
+
when /\d/
|
395
|
+
# Referring back to previously parsed type
|
396
|
+
# left and right are pushed as two separate strings
|
397
|
+
ct.left = pmt_ref[dt.to_i*2]
|
398
|
+
ct.right = pmt_ref[dt.to_i*2 + 1]
|
399
|
+
throw :done unless ct.left
|
400
|
+
add_pmt = false
|
401
|
+
|
402
|
+
when '$'
|
403
|
+
case sym.current.get_inc!
|
404
|
+
when '0'
|
405
|
+
throw :done unless ct.left = get_number(sym)
|
406
|
+
when 'D'
|
407
|
+
throw :done unless ptr = get_number(sym)
|
408
|
+
ct.left = sprintf("`template-parameter%s'", ptr)
|
409
|
+
when 'F'
|
410
|
+
throw :done unless p1 = get_number(sym)
|
411
|
+
throw :done unless p2 = get_number(sym)
|
412
|
+
ct.left = sprintf("{%s,%s}", p1, p2)
|
413
|
+
when 'G'
|
414
|
+
throw :done unless p1 = get_number(sym)
|
415
|
+
throw :done unless p2 = get_number(sym)
|
416
|
+
throw :done unless p3 = get_number(sym)
|
417
|
+
ct.left = sprintf("{%s,%s,%s}", p1, p2, p3)
|
418
|
+
when 'Q'
|
419
|
+
throw :done unless ptr = get_number(sym)
|
420
|
+
ct.left = sprintf("`non-type-template-parameter%s'", ptr)
|
421
|
+
when '$'
|
422
|
+
if (sym.current[0] == 'C')
|
423
|
+
ptr = ''
|
424
|
+
ptr_modif = ''
|
425
|
+
sym.current.inc!
|
426
|
+
throw :done unless get_modifier(sym, ptr, ptr_modif)
|
427
|
+
ptr = nil if ptr.empty?
|
428
|
+
ptr_modif = nil if ptr_modif.empty?
|
429
|
+
|
430
|
+
throw :done unless demangle_datatype(sym, ct, pmt_ref, in_args)
|
431
|
+
ct.left = sprintf("%s %s", ct.left, ptr)
|
432
|
+
end
|
433
|
+
end # case
|
434
|
+
else
|
435
|
+
err("Unknown type %c\n", dt)
|
436
|
+
end # case dt=...
|
437
|
+
|
438
|
+
if (add_pmt && pmt_ref && in_args)
|
439
|
+
# left and right are pushed as two separate strings
|
440
|
+
pmt_ref << (ct.left || "")
|
441
|
+
pmt_ref << (ct.right || "")
|
442
|
+
end # if
|
443
|
+
|
444
|
+
end # catch :done
|
445
|
+
|
446
|
+
return ct.left != nil
|
447
|
+
end # def demangle_datatype
|
448
|
+
|
449
|
+
def get_modified_type(ct, sym, pmt_ref, modif, in_args)
|
450
|
+
ptr_modif = ''
|
451
|
+
|
452
|
+
if sym.current[0] == 'E'
|
453
|
+
ptr_modif = " __ptr64"
|
454
|
+
sym.current.inc!
|
455
|
+
end
|
456
|
+
|
457
|
+
str_modif =
|
458
|
+
case modif
|
459
|
+
when 'A'; sprintf(" &%s", ptr_modif)
|
460
|
+
when 'B'; sprintf(" &%s volatile", ptr_modif)
|
461
|
+
when 'P'; sprintf(" *%s", ptr_modif)
|
462
|
+
when 'Q'; sprintf(" *%s const", ptr_modif)
|
463
|
+
when 'R'; sprintf(" *%s volatile", ptr_modif)
|
464
|
+
when 'S'; sprintf(" *%s const volatile", ptr_modif)
|
465
|
+
when '?'; ""
|
466
|
+
else
|
467
|
+
return false
|
468
|
+
end
|
469
|
+
|
470
|
+
modifier = ''
|
471
|
+
if get_modifier(sym, modifier, ptr_modif)
|
472
|
+
modifier = nil if modifier.empty?
|
473
|
+
ptr_modif = nil if ptr_modif.empty?
|
474
|
+
|
475
|
+
saved_stack = sym.stack.dup
|
476
|
+
sub_ct = DataType.new
|
477
|
+
|
478
|
+
# multidimensional arrays
|
479
|
+
if (sym.current[0] == 'Y')
|
480
|
+
sym.current.inc!
|
481
|
+
return false unless n1 = get_number(sym)
|
482
|
+
num = n1.to_i
|
483
|
+
|
484
|
+
if (str_modif[0] == ' ' && !modifier)
|
485
|
+
str_modif = str_modif[1..-1]
|
486
|
+
end
|
487
|
+
|
488
|
+
if (modifier)
|
489
|
+
str_modif = sprintf(" (%s%s)", modifier, str_modif)
|
490
|
+
modifier = nil
|
491
|
+
else
|
492
|
+
str_modif = sprintf(" (%s)", str_modif)
|
493
|
+
end
|
494
|
+
|
495
|
+
num.times do
|
496
|
+
str_modif = sprintf("%s[%s]", str_modif, get_number(sym))
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
# Recurse to get the referred-to type
|
501
|
+
return false unless demangle_datatype(sym, sub_ct, pmt_ref, false)
|
502
|
+
|
503
|
+
if modifier
|
504
|
+
ct.left = sprintf("%s %s%s", sub_ct.left, modifier, str_modif)
|
505
|
+
else
|
506
|
+
# don't insert a space between duplicate '*'
|
507
|
+
if (!in_args && str_modif[0] && str_modif[1] == '*' && sub_ct.left[-1] == '*')
|
508
|
+
str_modif = str_modif[1..-1]
|
509
|
+
end
|
510
|
+
ct.left = sprintf("%s%s", sub_ct.left, str_modif )
|
511
|
+
end
|
512
|
+
ct.right = sub_ct.right
|
513
|
+
sym.stack = saved_stack
|
514
|
+
end # if get_modifier
|
515
|
+
true
|
516
|
+
end # def get_modified_type
|
517
|
+
|
518
|
+
# Parses the type modifier.
|
519
|
+
# XXX ZZZ FIXME must check that ret & ptr_modif are not simple checked like
|
520
|
+
# if(!ret) or if(!ptr_modif)
|
521
|
+
def get_modifier(sym, ret, ptr_modif)
|
522
|
+
raise "ret must be a String" unless ret.is_a?(String)
|
523
|
+
raise "ptr_modif must be a String" unless ptr_modif.is_a?(String)
|
524
|
+
|
525
|
+
if sym.current[0] == 'E'
|
526
|
+
ptr_modif[0..-1] = "__ptr64"
|
527
|
+
sym.current.inc!
|
528
|
+
else
|
529
|
+
ptr_modif[0..-1] = ''
|
530
|
+
end
|
531
|
+
|
532
|
+
case sym.current.get_inc!
|
533
|
+
when 'A'; ret[0..-1] = '' # XXX original: *ret = NULL, may affect further checks
|
534
|
+
when 'B'; ret[0..-1] = "const"
|
535
|
+
when 'C'; ret[0..-1] = "volatile"
|
536
|
+
when 'D'; ret[0..-1] = "const volatile"
|
537
|
+
else
|
538
|
+
return false
|
539
|
+
end
|
540
|
+
|
541
|
+
true
|
542
|
+
end # def get_modifier
|
543
|
+
|
544
|
+
SIMPLE_TYPES = {
|
545
|
+
'C' => "signed char", 'D' => "char", 'E' => "unsigned char",
|
546
|
+
'F' => "short", 'G' => "unsigned short", 'H' => "int",
|
547
|
+
'I' => "unsigned int", 'J' => "long", 'K' => "unsigned long",
|
548
|
+
'M' => "float", 'N' => "double", 'O' => "long double",
|
549
|
+
'X' => "void", 'Z' => "..."
|
550
|
+
}
|
551
|
+
|
552
|
+
EXTENDED_TYPES = {
|
553
|
+
'D' => "__int8", 'E' => "unsigned __int8",
|
554
|
+
'F' => "__int16", 'G' => "unsigned __int16",
|
555
|
+
'H' => "__int32", 'I' => "unsigned __int32",
|
556
|
+
'J' => "__int64", 'K' => "unsigned __int64",
|
557
|
+
'L' => "__int128",'M' => "unsigned __int128",
|
558
|
+
'N' => "bool", 'W' => "wchar_t"
|
559
|
+
}
|
560
|
+
|
561
|
+
def get_simple_type c; SIMPLE_TYPES[c]; end
|
562
|
+
def get_extended_type c; EXTENDED_TYPES[c]; end
|
563
|
+
|
564
|
+
# Parses class as a list of parent-classes, terminated by '@' and stores the
|
565
|
+
# result in 'a' array. Each parent-classes, as well as the inner element
|
566
|
+
# (either field/method name or class name), are represented in the mangled
|
567
|
+
# name by a literal name ([a-zA-Z0-9_]+ terminated by '@') or a back reference
|
568
|
+
# ([0-9]) or a name with template arguments ('?$' literal name followed by the
|
569
|
+
# template argument list). The class name components appear in the reverse
|
570
|
+
# order in the mangled name, e.g aaa@bbb@ccc@@ will be demangled to
|
571
|
+
# ccc::bbb::aaa
|
572
|
+
def get_class(sym)
|
573
|
+
name = nil
|
574
|
+
while sym.current[0] != '@'
|
575
|
+
case sym.current[0]
|
576
|
+
when "\0",'',nil; return false
|
577
|
+
when /\d/;
|
578
|
+
# numbered backreference
|
579
|
+
name = sym.names[sym.current.get_inc!.to_i]
|
580
|
+
when '?'
|
581
|
+
case sym.current.inc_get!
|
582
|
+
when '$'
|
583
|
+
sym.current.inc!
|
584
|
+
return false unless name = get_template_name(sym)
|
585
|
+
sym.names << name
|
586
|
+
when '?'
|
587
|
+
saved_stack, saved_names = sym.stack.dup, sym.names.dup
|
588
|
+
sym.stack = []
|
589
|
+
name = "`#{sym.result}'" if symbol_demangle(sym)
|
590
|
+
sym.stack, sym.names = saved_stack, saved_names
|
591
|
+
else
|
592
|
+
return false unless name = get_number(sym)
|
593
|
+
name = "`#{name}'"
|
594
|
+
end # case
|
595
|
+
else
|
596
|
+
name = get_literal_string(sym)
|
597
|
+
end # case
|
598
|
+
return false unless name
|
599
|
+
sym.stack << name
|
600
|
+
end # while
|
601
|
+
sym.current.inc!
|
602
|
+
true
|
603
|
+
end # def get_class
|
604
|
+
|
605
|
+
# Gets the literal name from the current position in the mangled symbol to the
|
606
|
+
# first '@' character. It pushes the parsed name to the symbol names stack and
|
607
|
+
# returns a pointer to it or nil in when of an error.
|
608
|
+
def get_literal_string(sym)
|
609
|
+
ptr = sym.current.dup
|
610
|
+
idx = sym.current.index(/[^A-Za-z0-9_$]/) || sym.current.strlen
|
611
|
+
if sym.current[idx] == '@'
|
612
|
+
sym.current += idx+1
|
613
|
+
sym.names << ptr[0, sym.current - ptr - 1]
|
614
|
+
return sym.names.last
|
615
|
+
else
|
616
|
+
err("Failed at '%c' in %s\n", sym.current[0..-1], idx)
|
617
|
+
return nil
|
618
|
+
end
|
619
|
+
end # def get_literal_string
|
620
|
+
|
621
|
+
# Does the final parsing and handling for a function or a method in a class.
|
622
|
+
def handle_method(sym, cast_op)
|
623
|
+
access = member_type = name = modifier = call_conv = exported = args_str = name = nil
|
624
|
+
array_pmt = []
|
625
|
+
ret = false
|
626
|
+
ct_ret = DataType.new
|
627
|
+
|
628
|
+
accmem = sym.current.get_inc!
|
629
|
+
return unless accmem =~ /[A-Z]/
|
630
|
+
|
631
|
+
if sym.flags & UNDNAME_NO_ACCESS_SPECIFIERS == 0
|
632
|
+
case ((accmem.ord - 'A'.ord) / 8)
|
633
|
+
when 0; access = "private: "
|
634
|
+
when 1; access = "protected: "
|
635
|
+
when 2; access = "public: "
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
if sym.flags & UNDNAME_NO_MEMBER_TYPE == 0
|
640
|
+
if accmem <= 'X'
|
641
|
+
case ((accmem.ord - 'A'.ord) % 8)
|
642
|
+
when 2,3; member_type = "static "
|
643
|
+
when 4,5; member_type = "virtual "
|
644
|
+
when 6,7; member_type = "virtual "; access = "[thunk]:#{access}"
|
645
|
+
end
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
name = get_class_string(sym, 0)
|
650
|
+
|
651
|
+
if ((accmem.ord - 'A'.ord) % 8 == 6 || (accmem.ord - '8'.ord) % 8 == 7) # a thunk
|
652
|
+
name = sprintf("%s`adjustor{%s}' ", name, get_number(sym))
|
653
|
+
end
|
654
|
+
|
655
|
+
if (accmem <= 'X')
|
656
|
+
if (((accmem.ord - 'A'.ord) % 8) != 2 && ((accmem.ord - 'A'.ord) % 8) != 3)
|
657
|
+
modifier = ''; ptr_modif = ''
|
658
|
+
# Implicit 'this' pointer
|
659
|
+
# If there is an implicit this pointer, const modifier follows
|
660
|
+
return unless get_modifier(sym, modifier, ptr_modif)
|
661
|
+
modifier = nil if modifier.empty?
|
662
|
+
ptr_modif = nil if ptr_modif.empty?
|
663
|
+
if (modifier || ptr_modif)
|
664
|
+
modifier = "#{modifier} #{ptr_modif}"
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
return unless cc=get_calling_convention(sym.current.get_inc!, sym.flags)
|
670
|
+
call_conv, exported = cc
|
671
|
+
|
672
|
+
# Return type, or @ if 'void'
|
673
|
+
if (sym.current[0] == '@')
|
674
|
+
ct_ret.left = "void"
|
675
|
+
ct_ret.right = nil
|
676
|
+
sym.current.inc!
|
677
|
+
else
|
678
|
+
return unless demangle_datatype(sym, ct_ret, array_pmt, false)
|
679
|
+
end
|
680
|
+
|
681
|
+
if sym.flags & UNDNAME_NO_FUNCTION_RETURNS != 0
|
682
|
+
ct_ret.left = ct_ret.right = nil
|
683
|
+
end
|
684
|
+
|
685
|
+
if cast_op
|
686
|
+
name = [name, ct_ret.left, ct_ret.right].join
|
687
|
+
ct_ret.left = ct_ret.right = nil
|
688
|
+
end
|
689
|
+
|
690
|
+
saved_stack = sym.stack.dup
|
691
|
+
return unless args_str = get_args(sym, array_pmt, true, '(', ')')
|
692
|
+
if sym.flags & UNDNAME_NAME_ONLY != 0
|
693
|
+
args_str = modifier = nil
|
694
|
+
end
|
695
|
+
sym.stack = saved_stack
|
696
|
+
|
697
|
+
# Note: '()' after 'Z' means 'throws', but we don't care here
|
698
|
+
# Yet!!! FIXME
|
699
|
+
|
700
|
+
sym.result = [
|
701
|
+
access, member_type, ct_ret.left, (ct_ret.left && !ct_ret.right) ? " " :
|
702
|
+
nil, call_conv, call_conv ? " " : nil, exported, name, args_str, modifier,
|
703
|
+
ct_ret.right
|
704
|
+
].join
|
705
|
+
|
706
|
+
true
|
707
|
+
end # def handle_method
|
708
|
+
|
709
|
+
# From an array collected by get_class in sym.stack, constructs the
|
710
|
+
# corresponding string
|
711
|
+
def get_class_string(sym, start)
|
712
|
+
sym.stack[start..-1].reverse.join('::')
|
713
|
+
end
|
714
|
+
|
715
|
+
# Returns a static string corresponding to the calling convention described
|
716
|
+
# by char 'ch'. Sets export to true iff the calling convention is exported.
|
717
|
+
|
718
|
+
def get_calling_convention(ch, flags)
|
719
|
+
call_conv = exported = nil
|
720
|
+
|
721
|
+
unless ch
|
722
|
+
err("Unknown calling convention NULL\n")
|
723
|
+
return false
|
724
|
+
end
|
725
|
+
|
726
|
+
if (flags & (UNDNAME_NO_MS_KEYWORDS | UNDNAME_NO_ALLOCATION_LANGUAGE)) == 0
|
727
|
+
if (flags & UNDNAME_NO_LEADING_UNDERSCORES) != 0
|
728
|
+
exported = "dll_export " if (((ch.ord - 'A'.ord) % 2) == 1)
|
729
|
+
case ch
|
730
|
+
when 'A','B'; call_conv = "cdecl"
|
731
|
+
when 'C','D'; call_conv = "pascal"
|
732
|
+
when 'E','F'; call_conv = "thiscall"
|
733
|
+
when 'G','H'; call_conv = "stdcall"
|
734
|
+
when 'I','J'; call_conv = "fastcall"
|
735
|
+
when 'K','L'; # nothing
|
736
|
+
when 'M'; call_conv = "clrcall"
|
737
|
+
else
|
738
|
+
err("Unknown calling convention %c\n", ch)
|
739
|
+
return false
|
740
|
+
end
|
741
|
+
else
|
742
|
+
exported = "__dll_export " if (((ch.ord - 'A'.ord) % 2) == 1)
|
743
|
+
case ch
|
744
|
+
when 'A','B'; call_conv = "__cdecl"
|
745
|
+
when 'C','D'; call_conv = "__pascal"
|
746
|
+
when 'E','F'; call_conv = "__thiscall"
|
747
|
+
when 'G','H'; call_conv = "__stdcall"
|
748
|
+
when 'I','J'; call_conv = "__fastcall"
|
749
|
+
when 'K','L'; # nothing
|
750
|
+
when 'M'; call_conv = "__clrcall"
|
751
|
+
else
|
752
|
+
err("Unknown calling convention %c\n", ch)
|
753
|
+
return false
|
754
|
+
end
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
758
|
+
[call_conv, exported]
|
759
|
+
end # def get_calling_convention
|
760
|
+
|
761
|
+
# Parses a list of function/method arguments, creates a string corresponding
|
762
|
+
# to the arguments' list.
|
763
|
+
|
764
|
+
def get_args(sym, pmt_ref, z_term, open_char, close_char)
|
765
|
+
ct = DataType.new
|
766
|
+
arg_collect = []
|
767
|
+
args_str = last = nil
|
768
|
+
|
769
|
+
# Now come the function arguments
|
770
|
+
while sym.current[0]
|
771
|
+
# Decode each data type and append it to the argument list
|
772
|
+
if sym.current[0] == '@'
|
773
|
+
sym.current.inc!
|
774
|
+
break
|
775
|
+
end
|
776
|
+
return unless demangle_datatype(sym, ct, pmt_ref, true)
|
777
|
+
|
778
|
+
# 'void' terminates an argument list in a function
|
779
|
+
break if z_term && ct.left == "void"
|
780
|
+
arg_collect << [ct.left, ct.right].join
|
781
|
+
break if ct.left == "..."
|
782
|
+
end
|
783
|
+
|
784
|
+
# Functions are always terminated by 'Z'. If we made it this far and don't
|
785
|
+
# find it, we have incorrectly identified a data type.
|
786
|
+
|
787
|
+
return if z_term && sym.current.get_inc! != 'Z'
|
788
|
+
|
789
|
+
if arg_collect.empty? || arg_collect == ['void']
|
790
|
+
return [open_char, "void", close_char].join
|
791
|
+
end
|
792
|
+
|
793
|
+
args_str = arg_collect.join(', ')
|
794
|
+
# "...>>" => "...> >"
|
795
|
+
args_str << " " if close_char == '>' && args_str[-1] == '>'
|
796
|
+
|
797
|
+
[open_char, args_str, close_char].join
|
798
|
+
end # def get_args
|
799
|
+
|
800
|
+
# Wrapper around get_class and get_class_string.
|
801
|
+
def get_class_name sym
|
802
|
+
saved_stack = sym.stack.dup
|
803
|
+
s = nil
|
804
|
+
|
805
|
+
if get_class(sym)
|
806
|
+
s = get_class_string(sym, saved_stack.size) # ZZZ ???
|
807
|
+
end
|
808
|
+
sym.stack = saved_stack
|
809
|
+
s
|
810
|
+
end # def get_class_name
|
811
|
+
|
812
|
+
# Parses a name with a template argument list and returns it as a string.
|
813
|
+
# In a template argument list the back reference to the names table is
|
814
|
+
# separately created. '0' points to the class component name with the
|
815
|
+
# template arguments. We use the same stack array to hold the names but
|
816
|
+
# save/restore the stack state before/after parsing the template argument
|
817
|
+
# list.
|
818
|
+
def get_template_name sym
|
819
|
+
name = args = nil
|
820
|
+
saved_names = sym.names.dup
|
821
|
+
saved_stack = sym.stack.dup
|
822
|
+
array_pmt = []
|
823
|
+
|
824
|
+
sym.names = []
|
825
|
+
return unless name = get_literal_string(sym)
|
826
|
+
name << args if args = get_args(sym, array_pmt, false, '<', '>')
|
827
|
+
|
828
|
+
sym.names = saved_names
|
829
|
+
sym.stack = saved_stack
|
830
|
+
|
831
|
+
name
|
832
|
+
end # def get_template_name
|
833
|
+
|
834
|
+
def get_number sym
|
835
|
+
ptr = nil
|
836
|
+
sgn = false
|
837
|
+
|
838
|
+
if (sym.current[0] == '?')
|
839
|
+
sgn = true
|
840
|
+
sym.current.inc!
|
841
|
+
end
|
842
|
+
|
843
|
+
case sym.current[0]
|
844
|
+
when /[0-8]/
|
845
|
+
ptr = " "
|
846
|
+
ptr[0] = '-' if sgn
|
847
|
+
ptr[sgn ? 1 : 0] = (sym.current[0].ord + 1).chr
|
848
|
+
sym.current.inc!
|
849
|
+
ptr.strip!
|
850
|
+
when '9'
|
851
|
+
ptr = " "
|
852
|
+
ptr[0] = '-' if sgn
|
853
|
+
ptr[sgn ? 1 : 0] = '1'
|
854
|
+
ptr[sgn ? 2 : 1] = '0'
|
855
|
+
sym.current.inc!
|
856
|
+
ptr.strip!
|
857
|
+
when /[A-P]/
|
858
|
+
ret = 0
|
859
|
+
while sym.current[0] =~ /[A-P]/
|
860
|
+
ret *= 16
|
861
|
+
ret += sym.current.get_inc!.ord - 'A'.ord
|
862
|
+
end
|
863
|
+
return nil unless sym.current[0] == '@'
|
864
|
+
|
865
|
+
ptr = sprintf("%s%d", sgn ? "-" : "", ret)
|
866
|
+
sym.current.inc!
|
867
|
+
else
|
868
|
+
return nil
|
869
|
+
end
|
870
|
+
|
871
|
+
ptr
|
872
|
+
end # def get_number
|
873
|
+
|
874
|
+
# Does the final parsing and handling for a name with templates
|
875
|
+
|
876
|
+
def handle_template sym
|
877
|
+
assert(sym.current[0] == '$')
|
878
|
+
sym.current.inc!
|
879
|
+
return false unless name = get_literal_string(sym)
|
880
|
+
return false unless args = get_args(sym, nil, false, '<', '>')
|
881
|
+
sym.result = [name, args].join
|
882
|
+
true
|
883
|
+
end # def handle_template
|
884
|
+
|
885
|
+
# Does the final parsing and handling for a variable or a field in a class.
|
886
|
+
|
887
|
+
def handle_data sym
|
888
|
+
name = access = member_type = modifier = ptr_modif = nil
|
889
|
+
ct = DataType.new
|
890
|
+
ret = false
|
891
|
+
|
892
|
+
# 0 private static
|
893
|
+
# 1 protected static
|
894
|
+
# 2 public static
|
895
|
+
# 3 private non-static
|
896
|
+
# 4 protected non-static
|
897
|
+
# 5 public non-static
|
898
|
+
# 6 ?? static
|
899
|
+
# 7 ?? static
|
900
|
+
|
901
|
+
if sym.flags & UNDNAME_NO_ACCESS_SPECIFIERS == 0
|
902
|
+
# we only print the access for static members
|
903
|
+
case sym.current[0]
|
904
|
+
when '0'; access = "private: "
|
905
|
+
when '1'; access = "protected: "
|
906
|
+
when '2'; access = "public: "
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
910
|
+
if sym.flags & UNDNAME_NO_MEMBER_TYPE == 0
|
911
|
+
member_type = "static " if sym.current[0] =~ /[012]/
|
912
|
+
end
|
913
|
+
|
914
|
+
name = get_class_string(sym, 0)
|
915
|
+
|
916
|
+
case sym.current.get_inc!
|
917
|
+
when /[0-5]/
|
918
|
+
saved_stack = sym.stack.dup
|
919
|
+
modifier = ''; ptr_modif = ''
|
920
|
+
pmt = []
|
921
|
+
return unless demangle_datatype(sym, ct, pmt, false)
|
922
|
+
return unless get_modifier(sym, modifier, ptr_modif)
|
923
|
+
modifier = nil if modifier.empty?
|
924
|
+
ptr_modif = nil if ptr_modif.empty?
|
925
|
+
|
926
|
+
if modifier && ptr_modif
|
927
|
+
modifier += " " + ptr_modif
|
928
|
+
elsif !modifier
|
929
|
+
modifier = ptr_modif
|
930
|
+
end
|
931
|
+
sym.stack = saved_stack
|
932
|
+
|
933
|
+
when '6','7' # compiler generated static
|
934
|
+
ct.left = ct.right = nil
|
935
|
+
modifier = ''; ptr_modif = ''
|
936
|
+
return unless get_modifier(sym, modifier, ptr_modif)
|
937
|
+
modifier = nil if modifier.empty?
|
938
|
+
ptr_modif = nil if ptr_modif.empty?
|
939
|
+
|
940
|
+
if (sym.current[0] != '@')
|
941
|
+
return unless cls = get_class_name(sym)
|
942
|
+
ct.right = "{for `#{cls}'}"
|
943
|
+
end
|
944
|
+
when '8','9'
|
945
|
+
modifier = ct.left = ct.right = nil
|
946
|
+
else
|
947
|
+
return
|
948
|
+
end # case
|
949
|
+
|
950
|
+
if (sym.flags & UNDNAME_NAME_ONLY != 0)
|
951
|
+
ct.left = ct.right = modifier = nil
|
952
|
+
end
|
953
|
+
|
954
|
+
sym.result = [
|
955
|
+
access, member_type, ct.left, modifier && ct.left ? " " : nil, modifier,
|
956
|
+
modifier || ct.left ? " " : nil, name, ct.right
|
957
|
+
].join
|
958
|
+
|
959
|
+
true
|
960
|
+
end # def handle_data
|
961
|
+
|
962
|
+
end # class MSVC
|
963
|
+
|
964
|
+
######################################################################
|
965
|
+
|
966
|
+
if $0 == __FILE__
|
967
|
+
$:.unshift("./lib")
|
968
|
+
require 'unmangler/string_ptr'
|
969
|
+
require 'awesome_print'
|
970
|
+
require 'pp'
|
971
|
+
|
972
|
+
def check src, want, flags = 0
|
973
|
+
want = src if want == :bad
|
974
|
+
|
975
|
+
u = Unmangler::MSVC.new
|
976
|
+
got = nil
|
977
|
+
begin
|
978
|
+
got = u.unmangle(src, flags)
|
979
|
+
rescue
|
980
|
+
pp u
|
981
|
+
raise
|
982
|
+
end
|
983
|
+
if got == want
|
984
|
+
print ".".green
|
985
|
+
else
|
986
|
+
puts
|
987
|
+
puts "[!] src: #{src.inspect.gray}"
|
988
|
+
puts "[!] want: #{want.inspect.yellow}"
|
989
|
+
puts "[!] got: #{got.inspect.red}"
|
990
|
+
# pp u
|
991
|
+
# exit 1
|
992
|
+
end
|
993
|
+
end
|
994
|
+
|
995
|
+
if ARGV.any?
|
996
|
+
check ARGV[0], ARGV[1]
|
997
|
+
exit
|
998
|
+
end
|
999
|
+
|
1000
|
+
check "?h@@YAXH@Z", "void __cdecl h(int)"
|
1001
|
+
check "?AFXSetTopLevelFrame@@YAXPAVCFrameWnd@@@Z", "void __cdecl AFXSetTopLevelFrame(class CFrameWnd *)"
|
1002
|
+
check "??0_Lockit@std@@QAE@XZ", "public: __thiscall std::_Lockit::_Lockit(void)"
|
1003
|
+
|
1004
|
+
check "?SetAt@CString@@QAEXHD@Z", "public: void __thiscall CString::SetAt(int, char)"
|
1005
|
+
check "?LoadFrame@CMDIFrameWndEx@@UAEHIKPAVCWnd@@PAUCCreateContext@@@Z",
|
1006
|
+
"public: virtual int __thiscall CMDIFrameWndEx::LoadFrame(unsigned int, unsigned long, class CWnd *, struct CCreateContext *)"
|
1007
|
+
|
1008
|
+
check "??0DNameStatusNode@@AEAA@W4DNameStatus@@@Z",
|
1009
|
+
"private: __cdecl DNameStatusNode::DNameStatusNode(enum DNameStatus) __ptr64"
|
1010
|
+
|
1011
|
+
check "?Add@?$CArray@VCSize@@V1@@@QAEHVCSize@@@Z",
|
1012
|
+
"public: int __thiscall CArray<class CSize, class CSize>::Add(class CSize)"
|
1013
|
+
|
1014
|
+
check "??$_Char_traits_cat@U?$char_traits@D@std@@@std@@YA?AU_Secure_char_traits_tag@0@XZ",
|
1015
|
+
"struct std::_Secure_char_traits_tag __cdecl std::_Char_traits_cat<struct std::char_traits<char> >(void)"
|
1016
|
+
|
1017
|
+
check "?dtor$0@?0???0CDockSite@@QEAA@XZ@4HA",
|
1018
|
+
"int `public: __cdecl CDockSite::CDockSite(void) __ptr64'::`1'::dtor$0"
|
1019
|
+
|
1020
|
+
# bad examples
|
1021
|
+
check "?ProcessAndDestroyEdit", :bad
|
1022
|
+
check "?dtor$0@?0??Add@?$CArray@VXQATItem@XQAT@CMFCRibbonInfo@@V123@@@QEA", :bad
|
1023
|
+
|
1024
|
+
puts
|
1025
|
+
end
|