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