unmangler 0.0.1
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 +15 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +59 -0
- data/README.md.tpl +47 -0
- data/Rakefile +29 -0
- data/lib/unmangler.rb +23 -0
- data/lib/unmangler/borland.rb +1152 -0
- data/lib/unmangler/string_ptr.rb +112 -0
- data/lib/unmangler/version.rb +3 -0
- data/spec/borland_spec.rb +73 -0
- data/spec/spec_helper.rb +14 -0
- data/unmangler.gemspec +19 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NTJiYThhZDM5Mzk5ZWVmNTJhNDgxNDEzYTVhYzhhOTU0MjNjOTQ1Ng==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MWFiNWRjNGZiODgxOGNkZWQyYmJmMmM4MjNhMGZhOWRkMTVjNzMwZQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NGIzMGJmNjg5NzlhOTVlMWNjOTkzZTJlNzdkODcyMDZhNmQwODlmNzkwZDlk
|
10
|
+
NmY0ZGE1MDdlYjY1MmUyZDk2YWNiZjkwMGJhZTZmZmVjM2M4ZjQ5MjhhYjQ5
|
11
|
+
NTVlYTRjZTc0NmMwMDc4ZjY3NWViNzUzMjhjZjRlODdlYzdlZjI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YTY0ZWQ0ZDExZWUzMGRhZWVjYmFjNGY1ZmVkZjgyNmI3ZDhkMGQ0NDk2MGQ0
|
14
|
+
YTFiNTJiYTg3ZWM1NjI1Y2QyMWRhZTE2MWU2MmZiOTViN2FkNTg2YjZlMDA3
|
15
|
+
NjA0YTBlYTM3MTcyMGYzNjVhZDYyNDhhOTgyMWMzZDBkODEyMWU=
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Andrey "Zed" Zaikin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Unmangler
|
2
|
+
|
3
|
+
Unmangles mangled C++/Delphi names
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'unmangler'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install unmangler
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### Full unmangle #1
|
22
|
+
```ruby
|
23
|
+
puts Unmangler.unmangle "@afunc$qxzcupi"
|
24
|
+
|
25
|
+
# output:
|
26
|
+
afunc(const signed char, int *)
|
27
|
+
```
|
28
|
+
|
29
|
+
### Name-only unmangle #1
|
30
|
+
```ruby
|
31
|
+
puts Unmangler.unmangle "@afunc$qxzcupi", :args => false
|
32
|
+
|
33
|
+
# output:
|
34
|
+
afunc
|
35
|
+
```
|
36
|
+
|
37
|
+
### Full unmangle #2
|
38
|
+
```ruby
|
39
|
+
puts Unmangler.unmangle "@Forms@TApplication@SetTitle$qqrx17System@AnsiString"
|
40
|
+
|
41
|
+
# output:
|
42
|
+
__fastcall Forms::TApplication::SetTitle(const System::AnsiString)
|
43
|
+
```
|
44
|
+
|
45
|
+
### Name-only unmangle #2
|
46
|
+
```ruby
|
47
|
+
puts Unmangler.unmangle "@Forms@TApplication@SetTitle$qqrx17System@AnsiString", :args => false
|
48
|
+
|
49
|
+
# output:
|
50
|
+
Forms::TApplication::SetTitle
|
51
|
+
```
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create new Pull Request
|
data/README.md.tpl
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# Unmangler
|
2
|
+
|
3
|
+
Unmangles mangled C++/Delphi names
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'unmangler'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install unmangler
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### Full unmangle #1
|
22
|
+
```ruby
|
23
|
+
puts Unmangler.unmangle "@afunc$qxzcupi"
|
24
|
+
```
|
25
|
+
|
26
|
+
### Name-only unmangle #1
|
27
|
+
```ruby
|
28
|
+
puts Unmangler.unmangle "@afunc$qxzcupi", :args => false
|
29
|
+
```
|
30
|
+
|
31
|
+
### Full unmangle #2
|
32
|
+
```ruby
|
33
|
+
puts Unmangler.unmangle "@Forms@TApplication@SetTitle$qqrx17System@AnsiString"
|
34
|
+
```
|
35
|
+
|
36
|
+
### Name-only unmangle #2
|
37
|
+
```ruby
|
38
|
+
puts Unmangler.unmangle "@Forms@TApplication@SetTitle$qqrx17System@AnsiString", :args => false
|
39
|
+
```
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
46
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
47
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
desc "run specs"
|
5
|
+
RSpec::Core::RakeTask.new
|
6
|
+
|
7
|
+
task :default => :spec
|
8
|
+
|
9
|
+
desc "build readme"
|
10
|
+
task :readme do
|
11
|
+
tpl = File.read('README.md.tpl')
|
12
|
+
result = tpl.gsub(/^### ([^~`\n]+?)\n```ruby(.+?)^```/m) do |x|
|
13
|
+
title, code = $1, $2
|
14
|
+
|
15
|
+
File.open("tmp.rb", "w:utf-8") do |f|
|
16
|
+
f.puts "require 'unmangler'"
|
17
|
+
f.puts code
|
18
|
+
end
|
19
|
+
|
20
|
+
puts "[.] #{title} .. "
|
21
|
+
out = `ruby -Ilib tmp.rb`
|
22
|
+
exit unless $?.success?
|
23
|
+
|
24
|
+
x.sub code, code+"\n # output:\n"+out.split("\n").map{|x| " #{x}"}.join("\n")+"\n"
|
25
|
+
end
|
26
|
+
File.unlink("tmp.rb") rescue nil
|
27
|
+
File.open('README.md','w'){ |f| f << result }
|
28
|
+
#puts result
|
29
|
+
end
|
data/lib/unmangler.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'unmangler/version'
|
2
|
+
require 'unmangler/borland'
|
3
|
+
|
4
|
+
module Unmangler
|
5
|
+
class << self
|
6
|
+
def unmangle name, args={}
|
7
|
+
if name[0,1] == "@"
|
8
|
+
Unmangler::Borland.safe_unmangle name, args
|
9
|
+
# TODO: check if result is same as input
|
10
|
+
# and try to unmangle with MS if it is
|
11
|
+
elsif name[0,2] == '_Z'
|
12
|
+
# GCC ?
|
13
|
+
name
|
14
|
+
elsif name[0,1] == '?'
|
15
|
+
# MS ?
|
16
|
+
name
|
17
|
+
else
|
18
|
+
# return original name
|
19
|
+
name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,1152 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'unmangler/string_ptr'
|
4
|
+
|
5
|
+
# ported from Embarcadero RAD Studio XE3
|
6
|
+
# $(BDS)\source\cpprtl\Source\misc\unmangle.c
|
7
|
+
#
|
8
|
+
# most of the comments are from unmangle.c
|
9
|
+
|
10
|
+
module Unmangler; end
|
11
|
+
|
12
|
+
class Unmangler::Borland
|
13
|
+
attr_accessor :kind
|
14
|
+
|
15
|
+
UM_UNKNOWN = 0x00000000
|
16
|
+
|
17
|
+
UM_FUNCTION = 0x00000001
|
18
|
+
UM_CONSTRUCTOR = 0x00000002
|
19
|
+
UM_DESTRUCTOR = 0x00000003
|
20
|
+
UM_OPERATOR = 0x00000004
|
21
|
+
UM_CONVERSION = 0x00000005
|
22
|
+
UM_DATA = 0x00000006
|
23
|
+
UM_THUNK = 0x00000007
|
24
|
+
UM_TPDSC = 0x00000008
|
25
|
+
UM_VTABLE = 0x00000009
|
26
|
+
UM_VRDF_THUNK = 0x0000000a
|
27
|
+
UM_DYN_THUNK = 0x0000000b
|
28
|
+
|
29
|
+
UM_KINDMASK = 0x000000ff
|
30
|
+
|
31
|
+
# Modifier (is it a member, template?).
|
32
|
+
|
33
|
+
UM_QUALIFIED = 0x00000100
|
34
|
+
UM_TEMPLATE = 0x00000200
|
35
|
+
UM_VIRDEF_FLAG = 0x00000400
|
36
|
+
UM_FRIEND_LIST = 0x00000800
|
37
|
+
UM_CTCH_HNDL_TBL = 0x00001000
|
38
|
+
UM_OBJ_DEST_TBL = 0x00002000
|
39
|
+
UM_THROW_LIST = 0x00004000
|
40
|
+
UM_EXC_CTXT_TBL = 0x00008000
|
41
|
+
UM_LINKER_PROC = 0x00010000
|
42
|
+
UM_SPECMASK = 0x0001fc00
|
43
|
+
|
44
|
+
UM_MODMASK = 0x00ffff00
|
45
|
+
|
46
|
+
# Some @kind of error occurred.
|
47
|
+
|
48
|
+
UM_BUFOVRFLW = 0x01000000
|
49
|
+
UM_HASHTRUNC = 0x02000000
|
50
|
+
UM_ERROR = 0x04000000
|
51
|
+
|
52
|
+
UM_ERRMASK = 0x7f000000
|
53
|
+
|
54
|
+
# This symbol is not a mangled name.
|
55
|
+
|
56
|
+
UM_NOT_MANGLED = 0x80000000
|
57
|
+
|
58
|
+
MAXBUFFLEN = 8192 # maximum output length
|
59
|
+
|
60
|
+
# The mangler, when mangling argument types, will create
|
61
|
+
# backreferences if the type has already been seen. These take the
|
62
|
+
# form t?, where ? can be either 0-9, or a-z.
|
63
|
+
|
64
|
+
|
65
|
+
# New mangle scheme lengths:
|
66
|
+
# len == 254 ==> old hash
|
67
|
+
# len == 253 ==> new MD5 hash
|
68
|
+
# len < 253 ==> unhashed
|
69
|
+
|
70
|
+
QUALIFIER = '@'
|
71
|
+
ARGLIST = '$'
|
72
|
+
TMPLCODE = '%'
|
73
|
+
|
74
|
+
def input
|
75
|
+
# if @srcindx >= @hashstart
|
76
|
+
# raise 'UM_HASHTRUNC'
|
77
|
+
# else
|
78
|
+
c = @source[0]
|
79
|
+
#c == "\x00" ? 0 : c
|
80
|
+
# end
|
81
|
+
end
|
82
|
+
|
83
|
+
def advance
|
84
|
+
@source.inc!
|
85
|
+
input
|
86
|
+
end
|
87
|
+
|
88
|
+
def copy_char c
|
89
|
+
@target[0] = (c == 0 ? "\x00" : c)
|
90
|
+
@target.inc!
|
91
|
+
end
|
92
|
+
|
93
|
+
def copy_string s, len=0
|
94
|
+
if len == 0
|
95
|
+
@target << s
|
96
|
+
else
|
97
|
+
@target << s[0,len]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
alias :append :copy_string
|
102
|
+
|
103
|
+
# copy all remaining input until input end or any of term_chars is met
|
104
|
+
def copy_until *term_chars
|
105
|
+
term =
|
106
|
+
case term_chars.size
|
107
|
+
when 0; raise "no term_chars"
|
108
|
+
when 1; term_chars.first
|
109
|
+
else
|
110
|
+
Regexp.new( "[" + term_chars.map{ |c| Regexp::escape(c) }.join + "]" )
|
111
|
+
end
|
112
|
+
len = @source.index(term) || @source[0..-1].size
|
113
|
+
@target << @source[0,len]
|
114
|
+
@source += len
|
115
|
+
end
|
116
|
+
|
117
|
+
def strchr haystack, needle
|
118
|
+
if idx = haystack.index(needle)
|
119
|
+
StringPtr.new(haystack, idx)
|
120
|
+
else
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
OPS = {
|
126
|
+
"add" => "+", "adr" => "&", "and" => "&", "asg" => "=", "land"=> "&&",
|
127
|
+
"lor" => "||", "call"=> "()", "cmp" => "~", "fnc" => "()", "dec" => "--",
|
128
|
+
"div" => "/", "eql" => "==", "geq" => ">=", "gtr" => ">", "inc" => "++",
|
129
|
+
"ind" => "*", "leq" => "<=", "lsh" => "<<", "lss" => "<", "mod" => "%",
|
130
|
+
"mul" => "*", "neq" => "!=", "new" => "new","not" => "!", "or" => "|",
|
131
|
+
"rand"=> "&=", "rdiv"=> "/=", "rlsh"=> "<<=","rmin"=> "-=", "rmod"=> "%=",
|
132
|
+
"rmul"=> "*=", "ror" => "|=", "rplu"=> "+=", "rrsh"=> ">>=","rsh" => ">>",
|
133
|
+
"rxor"=> "^=", "subs"=> "[]", "sub" => "-", "xor" => "^", "arow"=> "->",
|
134
|
+
"nwa" => "new[]", "dele"=> "delete", "dla" => "delete[]"
|
135
|
+
}
|
136
|
+
|
137
|
+
def copy_op src
|
138
|
+
copy_string(OPS[src] || src)
|
139
|
+
end
|
140
|
+
|
141
|
+
def copy_return_type(start, callconv, regconv, process_return)
|
142
|
+
start = start.dup if start.is_a?(StringPtr)
|
143
|
+
|
144
|
+
ret_len = 0
|
145
|
+
# Process the return type of a function, and shuffle the output
|
146
|
+
# text around so it looks like the return type came first.
|
147
|
+
ret_type = @target.dup
|
148
|
+
|
149
|
+
unless [0,nil,false].include?(process_return)
|
150
|
+
copy_type(@target, 0)
|
151
|
+
copy_char(' ')
|
152
|
+
end
|
153
|
+
|
154
|
+
copy_string(callconv) if callconv
|
155
|
+
copy_string(regconv) if regconv
|
156
|
+
|
157
|
+
ret_len = @target - ret_type
|
158
|
+
|
159
|
+
# Set up the return type to have a space after it.
|
160
|
+
|
161
|
+
# "foo((*)(float, int)double "
|
162
|
+
|
163
|
+
buff = ret_type[0, ret_len]
|
164
|
+
start[ret_len, ret_type-start] = start[0, ret_type-start]
|
165
|
+
start[0, ret_len] = buff[0, ret_len]
|
166
|
+
|
167
|
+
# "foo(double (*)(float, int)"
|
168
|
+
|
169
|
+
# If we are inserting this return type at the very beginning of a
|
170
|
+
# string, it means the location of all the qualifier names is
|
171
|
+
# about to move.
|
172
|
+
|
173
|
+
unless [0,nil,false].include?(@adjust_quals)
|
174
|
+
@namebase += ret_len if @namebase
|
175
|
+
@qualend += ret_len if @qualend
|
176
|
+
@prevqual += ret_len if @prevqual
|
177
|
+
@base_name+= ret_len if @base_name
|
178
|
+
@base_end += ret_len if @base_end
|
179
|
+
end
|
180
|
+
end # def copy_return_type
|
181
|
+
|
182
|
+
def copy_type(start, arglvl)
|
183
|
+
start = start.dup if start.is_a?(StringPtr)
|
184
|
+
|
185
|
+
tname = nil
|
186
|
+
c = input()
|
187
|
+
is_const = false
|
188
|
+
is_volatile = false
|
189
|
+
is_signed = false
|
190
|
+
is_unsigned = false
|
191
|
+
maxloop = 101
|
192
|
+
savedsavechar = nil
|
193
|
+
|
194
|
+
arglvl =
|
195
|
+
case arglvl
|
196
|
+
when 0, nil, false; false
|
197
|
+
else true
|
198
|
+
end
|
199
|
+
|
200
|
+
loop do
|
201
|
+
assert((maxloop-=1) > 0)
|
202
|
+
case c
|
203
|
+
when 'u'; is_unsigned = true
|
204
|
+
when 'z'; is_signed = true
|
205
|
+
when 'x'; is_const = true
|
206
|
+
when 'w'; is_volatile = true
|
207
|
+
when 'y'
|
208
|
+
# 'y' for closure is followed by 'f' or 'n'
|
209
|
+
c = advance()
|
210
|
+
assert(c == 'f' || c == 'n')
|
211
|
+
copy_string("__closure")
|
212
|
+
else
|
213
|
+
break
|
214
|
+
end
|
215
|
+
|
216
|
+
c = advance()
|
217
|
+
end # loop
|
218
|
+
|
219
|
+
if isdigit(c) # enum or class name
|
220
|
+
i = 0
|
221
|
+
|
222
|
+
begin # compute length
|
223
|
+
i = i * 10 + (c.ord - '0'.ord)
|
224
|
+
c = advance()
|
225
|
+
end while isdigit(c)
|
226
|
+
|
227
|
+
# Output whether this class name was const or volatile.
|
228
|
+
|
229
|
+
# These were already printed (see [BCB-265738])
|
230
|
+
#if 0
|
231
|
+
# if (is_const) copy_string("const ")
|
232
|
+
# if (is_volatile) copy_string("volatile ")
|
233
|
+
#endif
|
234
|
+
|
235
|
+
# ZZZ
|
236
|
+
s0 = @source.string.dup
|
237
|
+
@source[i] = "\x00"
|
238
|
+
@source.trim!
|
239
|
+
copy_name(0)
|
240
|
+
@source.string = s0
|
241
|
+
@target.trim!
|
242
|
+
return
|
243
|
+
end # if isdigit(c)
|
244
|
+
|
245
|
+
@savechar = c
|
246
|
+
tname = nil
|
247
|
+
|
248
|
+
if c == 'M' # member pointer
|
249
|
+
name = @target.dup
|
250
|
+
# We call 'copy_type' because it knows how to extract
|
251
|
+
# length-prefixed names.
|
252
|
+
advance()
|
253
|
+
copy_type(@target, 0)
|
254
|
+
len = @target - name
|
255
|
+
len = MAXBUFFLEN - 1 if (len > MAXBUFFLEN - 1)
|
256
|
+
strncpy(buff, name, len)
|
257
|
+
buff[len] = 0
|
258
|
+
@target = name
|
259
|
+
end
|
260
|
+
|
261
|
+
case c
|
262
|
+
when 'v'; tname = "void"
|
263
|
+
when 'c'; tname = "char"
|
264
|
+
when 'b'; tname = "wchar_t"
|
265
|
+
when 's'; tname = "short"
|
266
|
+
when 'i'; tname = "int"
|
267
|
+
when 'l'; tname = "long"
|
268
|
+
when 'f'; tname = "float"
|
269
|
+
when 'd'; tname = "double"
|
270
|
+
when 'g'; tname = "long double"
|
271
|
+
when 'j'; tname = "long long"
|
272
|
+
when 'o'; tname = "bool"
|
273
|
+
when 'e'; tname = "..."
|
274
|
+
|
275
|
+
when 'C' # C++ wide char
|
276
|
+
c = advance()
|
277
|
+
if (c == 's')
|
278
|
+
tname = "char16_t"
|
279
|
+
elsif (c == 'i')
|
280
|
+
tname = "char32_t"
|
281
|
+
else
|
282
|
+
raise "Unknown type"
|
283
|
+
end
|
284
|
+
|
285
|
+
when 'M','r','h','p' # member pointer, reference, rvalue reference, pointer
|
286
|
+
if (@savechar == 'M')
|
287
|
+
c = input() # [BTS-??????]
|
288
|
+
case c
|
289
|
+
when 'x'; is_const = true; c = advance() # [BCB-272500]
|
290
|
+
when 'w'; is_volatile = true; c = advance()
|
291
|
+
end
|
292
|
+
else
|
293
|
+
c = advance()
|
294
|
+
end
|
295
|
+
|
296
|
+
if (c == 'q') # function pointer
|
297
|
+
copy_char('(')
|
298
|
+
|
299
|
+
if (@savechar == 'M')
|
300
|
+
copy_string(buff)
|
301
|
+
append "::"
|
302
|
+
end
|
303
|
+
|
304
|
+
append "*)"
|
305
|
+
|
306
|
+
@savechar = c
|
307
|
+
end
|
308
|
+
|
309
|
+
savedsavechar = @savechar; # [BTS-263572]
|
310
|
+
copy_type(start, 0)
|
311
|
+
@savechar = savedsavechar
|
312
|
+
|
313
|
+
case @savechar
|
314
|
+
when 'r'; copy_char('&')
|
315
|
+
when 'h'; append('&&')
|
316
|
+
when 'p'; append(' *')
|
317
|
+
when 'M'
|
318
|
+
assert(buff[0])
|
319
|
+
copy_char(' ')
|
320
|
+
copy_string(buff)
|
321
|
+
append '::*'
|
322
|
+
end
|
323
|
+
|
324
|
+
when 'a' # array
|
325
|
+
dims = ''
|
326
|
+
|
327
|
+
begin
|
328
|
+
c = advance()
|
329
|
+
dims << '['
|
330
|
+
c = advance() if (c == '0') # 0 size means unspecified
|
331
|
+
while (c != '$') # collect size, up to '$'
|
332
|
+
dims << c
|
333
|
+
c = advance()
|
334
|
+
end
|
335
|
+
assert(c == '$')
|
336
|
+
c = advance()
|
337
|
+
dims << ']'
|
338
|
+
end while (c == 'a') # collect all dimensions
|
339
|
+
|
340
|
+
copy_type(@target, 0)
|
341
|
+
copy_string(dims)
|
342
|
+
|
343
|
+
when 'q' # function
|
344
|
+
callconv = regconv = hasret = save_adjqual = nil
|
345
|
+
|
346
|
+
# We want the return type first, but find it last. So we emit
|
347
|
+
# all but the return type, get the return type, then shuffle
|
348
|
+
# to get them in the right place.
|
349
|
+
|
350
|
+
loop do
|
351
|
+
break if (advance() != 'q')
|
352
|
+
|
353
|
+
case advance()
|
354
|
+
when 'c'; callconv = "__cdecl "
|
355
|
+
when 'p'; callconv = "__pascal "
|
356
|
+
when 'r'; callconv = "__fastcall "
|
357
|
+
when 'f'; callconv = "__fortran "
|
358
|
+
when 's'; callconv = "__stdcall "
|
359
|
+
when 'y'; callconv = "__syscall "
|
360
|
+
when 'i'; callconv = "__interrupt "
|
361
|
+
when 'g'; regconv = "__saveregs "
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
save_adjqual = @adjust_quals
|
366
|
+
@adjust_quals = 0
|
367
|
+
|
368
|
+
copy_char('(')
|
369
|
+
copy_args('$', 0)
|
370
|
+
copy_char(')')
|
371
|
+
|
372
|
+
@adjust_quals = save_adjqual
|
373
|
+
|
374
|
+
hasret = input() == '$'
|
375
|
+
advance() if hasret
|
376
|
+
|
377
|
+
if (hasret || callconv || regconv)
|
378
|
+
copy_return_type(start, callconv, regconv, hasret)
|
379
|
+
end
|
380
|
+
|
381
|
+
when ARGLIST # template arg list
|
382
|
+
# break
|
383
|
+
when TMPLCODE # template reference
|
384
|
+
# break
|
385
|
+
|
386
|
+
else
|
387
|
+
raise "Unknown type: #{c.inspect}"
|
388
|
+
end # case
|
389
|
+
|
390
|
+
if (tname)
|
391
|
+
copy_string("const ") if is_const
|
392
|
+
copy_string("volatile ") if is_volatile
|
393
|
+
copy_string("signed ") if is_signed
|
394
|
+
copy_string("unsigned ") if is_unsigned
|
395
|
+
copy_string(tname) if (!arglvl || @savechar != 'v')
|
396
|
+
advance()
|
397
|
+
else
|
398
|
+
copy_string(" const") if is_const
|
399
|
+
copy_string(" volatile") if is_volatile
|
400
|
+
end
|
401
|
+
end # def copy_type
|
402
|
+
|
403
|
+
def copy_delphi4args(_end, tmplargs)
|
404
|
+
first = true
|
405
|
+
_begin = start = nil
|
406
|
+
termchar = 0
|
407
|
+
|
408
|
+
tmplargs =
|
409
|
+
case tmplargs
|
410
|
+
when 0, nil, false; false
|
411
|
+
else true
|
412
|
+
end
|
413
|
+
|
414
|
+
c = input()
|
415
|
+
while (c && c != _end)
|
416
|
+
if first
|
417
|
+
first = false
|
418
|
+
else
|
419
|
+
append ', '
|
420
|
+
end
|
421
|
+
|
422
|
+
_begin = @source.dup
|
423
|
+
start = @target.dup
|
424
|
+
|
425
|
+
advance() # skip the @kind character
|
426
|
+
|
427
|
+
# loop is for fallthrough emulation
|
428
|
+
loop do
|
429
|
+
case c
|
430
|
+
when 't'
|
431
|
+
copy_type(@target, ! tmplargs)
|
432
|
+
break
|
433
|
+
|
434
|
+
when 'T'
|
435
|
+
copy_string("<type ")
|
436
|
+
termchar = '>'
|
437
|
+
c = 'i'; redo # fall through
|
438
|
+
|
439
|
+
when 'i'
|
440
|
+
if _begin[0,5] == '4bool'
|
441
|
+
if input() == '0'
|
442
|
+
copy_string("false")
|
443
|
+
else
|
444
|
+
copy_string("true")
|
445
|
+
end
|
446
|
+
advance()
|
447
|
+
break
|
448
|
+
else
|
449
|
+
# XXX ZZZ fall through, but not sure that its intended behaviour
|
450
|
+
# in original code
|
451
|
+
c = 'j'; redo
|
452
|
+
end
|
453
|
+
|
454
|
+
when 'j','g','e'
|
455
|
+
copy_type(@target, ! tmplargs)
|
456
|
+
@target = start.dup
|
457
|
+
assert(input() == '$'); advance()
|
458
|
+
copy_until('$', TMPLCODE)
|
459
|
+
copy_char(termchar) if termchar
|
460
|
+
break
|
461
|
+
|
462
|
+
when 'm'
|
463
|
+
copy_type(@target, ! tmplargs)
|
464
|
+
@target = start.dup
|
465
|
+
assert(input() == '$'); advance()
|
466
|
+
|
467
|
+
copy_until('$')
|
468
|
+
append '::*'
|
469
|
+
copy_until('$', TMPLCODE)
|
470
|
+
break
|
471
|
+
|
472
|
+
else
|
473
|
+
raise "Unknown template arg @kind"
|
474
|
+
end # case
|
475
|
+
end # loop
|
476
|
+
|
477
|
+
c = input()
|
478
|
+
if (c != _end)
|
479
|
+
assert(c == '$')
|
480
|
+
c = advance()
|
481
|
+
end
|
482
|
+
end # while c && c != _end
|
483
|
+
end
|
484
|
+
|
485
|
+
PEntry = Struct.new :targpos, :len
|
486
|
+
|
487
|
+
def copy_args(_end, tmplargs)
|
488
|
+
c = input()
|
489
|
+
first = true
|
490
|
+
_begin = start = nil
|
491
|
+
scanned = 0
|
492
|
+
param_table = []
|
493
|
+
|
494
|
+
tmplargs =
|
495
|
+
case tmplargs
|
496
|
+
when 0, nil, false; false
|
497
|
+
else true
|
498
|
+
end
|
499
|
+
|
500
|
+
while (c && c != _end)
|
501
|
+
if first
|
502
|
+
first = false
|
503
|
+
else
|
504
|
+
append ', '
|
505
|
+
end
|
506
|
+
|
507
|
+
_begin = @source.dup
|
508
|
+
start = @target.dup
|
509
|
+
|
510
|
+
param_table << PEntry.new
|
511
|
+
param_table.last.targpos = @target.dup
|
512
|
+
|
513
|
+
scanned = 0
|
514
|
+
|
515
|
+
while (c == 'x' || c == 'w')
|
516
|
+
# Decode 'const'/'volatile' modifiers [BCB-265738]
|
517
|
+
case c
|
518
|
+
when 'x'; copy_string("const ")
|
519
|
+
when 'w'; copy_string("volatile ")
|
520
|
+
end
|
521
|
+
scanned = 1
|
522
|
+
c = advance()
|
523
|
+
end
|
524
|
+
|
525
|
+
if (scanned && c != 't')
|
526
|
+
@source = _begin.dup
|
527
|
+
end
|
528
|
+
|
529
|
+
if (c != 't')
|
530
|
+
copy_type(@target, ! tmplargs)
|
531
|
+
else
|
532
|
+
c = advance()
|
533
|
+
ptindex = c.to_i(16) - 1
|
534
|
+
assert(param_table[ptindex].targpos)
|
535
|
+
assert(param_table[ptindex].len > 0)
|
536
|
+
copy_string param_table[ptindex].targpos[0, param_table[ptindex].len]
|
537
|
+
advance()
|
538
|
+
end
|
539
|
+
|
540
|
+
param_table.last.len = @target - param_table.last.targpos
|
541
|
+
|
542
|
+
c = input()
|
543
|
+
|
544
|
+
if (tmplargs && c == '$') # non-type template argument
|
545
|
+
termchar = 0
|
546
|
+
@target = start.dup
|
547
|
+
c = advance()
|
548
|
+
advance()
|
549
|
+
loop do # loop is for fall through emulation
|
550
|
+
case c
|
551
|
+
when 'T'
|
552
|
+
copy_string("<type ")
|
553
|
+
termchar = '>'
|
554
|
+
c = 'i'; redo # fall through
|
555
|
+
|
556
|
+
when 'i'
|
557
|
+
if _begin[0,5] == "4bool"
|
558
|
+
if (input() == '0')
|
559
|
+
copy_string("false")
|
560
|
+
else
|
561
|
+
copy_string("true")
|
562
|
+
end
|
563
|
+
advance()
|
564
|
+
break
|
565
|
+
end
|
566
|
+
c = 'j'; redo # fall through
|
567
|
+
|
568
|
+
when 'j','g','e'
|
569
|
+
copy_until('$')
|
570
|
+
copy_char(termchar) if (termchar)
|
571
|
+
break
|
572
|
+
|
573
|
+
when 'm'
|
574
|
+
copy_until('$')
|
575
|
+
append '::*'
|
576
|
+
copy_until('$')
|
577
|
+
break
|
578
|
+
|
579
|
+
else
|
580
|
+
raise "Unknown template arg @kind"
|
581
|
+
end # case
|
582
|
+
end # loop
|
583
|
+
|
584
|
+
assert(input() == '$')
|
585
|
+
c = advance()
|
586
|
+
end # if
|
587
|
+
end # while (c && c != _end)
|
588
|
+
end # def copy_args
|
589
|
+
|
590
|
+
# parse template name and arguments according to the grammar:
|
591
|
+
# tmpl_args:
|
592
|
+
# % generic_name args %
|
593
|
+
# args:
|
594
|
+
# $ new_args
|
595
|
+
# bcb3_args
|
596
|
+
def copy_tmpl_args
|
597
|
+
c = input()
|
598
|
+
save_setqual = nil
|
599
|
+
isDelphi4name = (c == 'S' || c == 'D') && (@source =~ /\A(Set|DynamicArray|SmallString|DelphiInterface)\$/)
|
600
|
+
|
601
|
+
# Output the base name of the template. We use 'copy_name' instead of
|
602
|
+
# 'copy_until', since this could be a template constructor name, f.ex.
|
603
|
+
|
604
|
+
copy_name(1)
|
605
|
+
assert(input() == ARGLIST)
|
606
|
+
advance()
|
607
|
+
|
608
|
+
# using @target[-1] will be ambiguous for ruby's string[-1] - last char of string
|
609
|
+
copy_char(' ') if (@target-1)[0] == '<'
|
610
|
+
copy_char('<')
|
611
|
+
|
612
|
+
# Copy the template arguments over. Also, save the
|
613
|
+
# '@set_qual' variable, since we don't want to mix up the
|
614
|
+
# status of the currently known qualifier name with a
|
615
|
+
# name from a template argument, for example.
|
616
|
+
|
617
|
+
save_setqual = @set_qual
|
618
|
+
@set_qual = 0
|
619
|
+
|
620
|
+
if isDelphi4name
|
621
|
+
copy_delphi4args(TMPLCODE, 1)
|
622
|
+
else
|
623
|
+
copy_args(TMPLCODE, 1)
|
624
|
+
end
|
625
|
+
|
626
|
+
@set_qual = save_setqual
|
627
|
+
|
628
|
+
# using @target[-1] will be ambiguous for ruby's string[-1] - last char of string
|
629
|
+
copy_char(' ') if (@target-1)[0] == '>'
|
630
|
+
copy_char('>')
|
631
|
+
|
632
|
+
assert(input() == TMPLCODE)
|
633
|
+
advance()
|
634
|
+
end
|
635
|
+
|
636
|
+
def isdigit c
|
637
|
+
c =~ /\A\d\Z/
|
638
|
+
end
|
639
|
+
|
640
|
+
def assert cond
|
641
|
+
unless cond
|
642
|
+
# puts
|
643
|
+
# puts
|
644
|
+
# p @source
|
645
|
+
# p @source[0..-1]
|
646
|
+
# puts
|
647
|
+
# p @target
|
648
|
+
# puts
|
649
|
+
raise
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
def copy_name tmplname
|
654
|
+
start = save_setqual = nil
|
655
|
+
c = input()
|
656
|
+
|
657
|
+
# Start outputting the qualifier names and the base name.
|
658
|
+
|
659
|
+
while true
|
660
|
+
if @set_qual
|
661
|
+
@base_name = @target.dup
|
662
|
+
end
|
663
|
+
|
664
|
+
# Examine the string to see what this is. Either it's a
|
665
|
+
# qualifier name, a member name, a function name, a template
|
666
|
+
# name, or a special name. We wouldn't be here if this were a
|
667
|
+
# regular name.
|
668
|
+
|
669
|
+
if isdigit(c)
|
670
|
+
# If there's a number at the beginning of a name, it
|
671
|
+
# could only be a vtable symbol flag.
|
672
|
+
|
673
|
+
flags = c[0].ord - '0'.ord + 1
|
674
|
+
|
675
|
+
@vtbl_flags << "huge" if( flags & 1 != 0 )
|
676
|
+
@vtbl_flags << "fastthis" if( flags & 2 != 0 )
|
677
|
+
@vtbl_flags << "rtti" if( flags & 4 != 0 )
|
678
|
+
|
679
|
+
@kind = (@kind & ~UM_KINDMASK) | UM_VTABLE
|
680
|
+
|
681
|
+
c = advance()
|
682
|
+
assert(c == 0 || c == '$')
|
683
|
+
end
|
684
|
+
|
685
|
+
case c
|
686
|
+
when '#' # special symbol used for cond syms
|
687
|
+
c = advance()
|
688
|
+
if c == '$'
|
689
|
+
assert(advance() == 'c')
|
690
|
+
assert(advance() == 'f')
|
691
|
+
assert(advance() == '$')
|
692
|
+
assert(advance() == '@')
|
693
|
+
|
694
|
+
copy_string("__vdflg__ ")
|
695
|
+
advance()
|
696
|
+
copy_name(0)
|
697
|
+
|
698
|
+
@kind |= UM_VIRDEF_FLAG
|
699
|
+
end
|
700
|
+
return
|
701
|
+
|
702
|
+
when QUALIFIER # virdef flag or linker proc
|
703
|
+
advance()
|
704
|
+
copy_string("__linkproc__ ")
|
705
|
+
copy_name(0)
|
706
|
+
@kind |= UM_LINKER_PROC
|
707
|
+
return
|
708
|
+
|
709
|
+
when TMPLCODE # template name
|
710
|
+
advance()
|
711
|
+
copy_tmpl_args()
|
712
|
+
|
713
|
+
if (input() != QUALIFIER)
|
714
|
+
@kind |= UM_TEMPLATE
|
715
|
+
end
|
716
|
+
|
717
|
+
when ARGLIST # special name, or arglist
|
718
|
+
return unless [0,nil,false].include?(tmplname)
|
719
|
+
|
720
|
+
c = advance()
|
721
|
+
if c == 'x'
|
722
|
+
c = advance()
|
723
|
+
if c == 'p' || c == 't'
|
724
|
+
assert(advance() == ARGLIST)
|
725
|
+
advance()
|
726
|
+
copy_string("__tpdsc__ ")
|
727
|
+
copy_type(@target, 0)
|
728
|
+
@kind = (@kind & ~UM_KINDMASK) | UM_TPDSC
|
729
|
+
return
|
730
|
+
else
|
731
|
+
raise "What happened?"
|
732
|
+
end
|
733
|
+
end # if c == 'x'
|
734
|
+
|
735
|
+
if c == 'b'
|
736
|
+
c = advance()
|
737
|
+
start = @source.dup
|
738
|
+
|
739
|
+
if (c == 'c' || c == 'd') && advance() == 't' && advance() == 'r'
|
740
|
+
assert(advance() == ARGLIST)
|
741
|
+
|
742
|
+
# The actual outputting of the name will happen
|
743
|
+
# outside of this function, to be sure that we
|
744
|
+
# don't include any special name characters.
|
745
|
+
|
746
|
+
if (c == 'c')
|
747
|
+
@kind = (@kind & ~UM_KINDMASK) | UM_CONSTRUCTOR
|
748
|
+
else
|
749
|
+
@kind = (@kind & ~UM_KINDMASK) | UM_DESTRUCTOR
|
750
|
+
end
|
751
|
+
else
|
752
|
+
@source = start.dup
|
753
|
+
copy_string("operator ")
|
754
|
+
start = @target.dup
|
755
|
+
copy_until(ARGLIST)
|
756
|
+
@target = start # no dup() here intentionally
|
757
|
+
# copy_op now will overwrite already copied encoded operator name
|
758
|
+
# i.e. "subs" => "[]"
|
759
|
+
copy_op start[0..-1]
|
760
|
+
# trim string if decoded operator name was shorter than encoded
|
761
|
+
@target[0..-1] = ''
|
762
|
+
@kind = (@kind & ~UM_KINDMASK) | UM_OPERATOR
|
763
|
+
end
|
764
|
+
|
765
|
+
elsif (c == 'o')
|
766
|
+
advance()
|
767
|
+
copy_string("operator ")
|
768
|
+
save_setqual = @set_qual
|
769
|
+
@set_qual = 0
|
770
|
+
copy_type(@target, 0)
|
771
|
+
@set_qual = save_setqual
|
772
|
+
assert(input() == ARGLIST)
|
773
|
+
@kind = (@kind & ~UM_KINDMASK) | UM_CONVERSION
|
774
|
+
|
775
|
+
elsif (c == 'v' || c == 'd')
|
776
|
+
tkind = c
|
777
|
+
c = advance()
|
778
|
+
if (tkind == 'v' && c == 's')
|
779
|
+
c = advance()
|
780
|
+
assert(c == 'f' || c == 'n')
|
781
|
+
advance()
|
782
|
+
copy_string("__vdthk__")
|
783
|
+
@kind = (@kind & ~UM_KINDMASK) | UM_VRDF_THUNK
|
784
|
+
elsif (c == 'c')
|
785
|
+
c = advance()
|
786
|
+
assert(isdigit(c))
|
787
|
+
c = advance()
|
788
|
+
assert(c == '$')
|
789
|
+
c = advance()
|
790
|
+
|
791
|
+
copy_string("__thunk__ [")
|
792
|
+
@kind = (@kind & ~UM_KINDMASK) |
|
793
|
+
(tkind == 'v' ? UM_THUNK : UM_DYN_THUNK)
|
794
|
+
|
795
|
+
copy_char(c)
|
796
|
+
copy_char(',')
|
797
|
+
|
798
|
+
while ((c = advance()) != '$'); copy_char(c); end
|
799
|
+
copy_char(',')
|
800
|
+
while ((c = advance()) != '$'); copy_char(c); end
|
801
|
+
copy_char(',')
|
802
|
+
while ((c = advance()) != '$'); copy_char(c); end
|
803
|
+
copy_char(']')
|
804
|
+
|
805
|
+
advance() # skip last '$'
|
806
|
+
return
|
807
|
+
end
|
808
|
+
else
|
809
|
+
raise "Unknown special name"
|
810
|
+
end
|
811
|
+
|
812
|
+
when '_'
|
813
|
+
start = @source.dup
|
814
|
+
|
815
|
+
if advance() == '$'
|
816
|
+
c = advance()
|
817
|
+
|
818
|
+
# At the moment there are five @kind of special names:
|
819
|
+
# frndl FL friend list
|
820
|
+
# chtbl CH catch handler table
|
821
|
+
# odtbl DC object destructor table
|
822
|
+
# thrwl TL throw list
|
823
|
+
# ectbl ETC exception context table
|
824
|
+
|
825
|
+
append '__'
|
826
|
+
|
827
|
+
case @source[0,2]
|
828
|
+
when 'FL'
|
829
|
+
copy_string("frndl")
|
830
|
+
@kind |= UM_FRIEND_LIST
|
831
|
+
|
832
|
+
when 'CH'
|
833
|
+
copy_string("chtbl")
|
834
|
+
@kind |= UM_CTCH_HNDL_TBL
|
835
|
+
|
836
|
+
when 'DC'
|
837
|
+
copy_string("odtbl")
|
838
|
+
@kind |= UM_OBJ_DEST_TBL
|
839
|
+
|
840
|
+
when 'TL'
|
841
|
+
copy_string("thrwl")
|
842
|
+
@kind |= UM_THROW_LIST
|
843
|
+
|
844
|
+
when 'EC'
|
845
|
+
copy_string("ectbl")
|
846
|
+
@kind |= UM_EXC_CTXT_TBL
|
847
|
+
end
|
848
|
+
|
849
|
+
append '__ '
|
850
|
+
|
851
|
+
while (c >= 'A' && c <= 'Z'); c = advance(); end
|
852
|
+
|
853
|
+
assert(c == '$')
|
854
|
+
assert(advance() == '@')
|
855
|
+
advance()
|
856
|
+
|
857
|
+
copy_name(0)
|
858
|
+
|
859
|
+
return
|
860
|
+
end # if advance() == '$'
|
861
|
+
|
862
|
+
@source = start.dup
|
863
|
+
copy_until(QUALIFIER, ARGLIST)
|
864
|
+
|
865
|
+
else
|
866
|
+
# default case
|
867
|
+
copy_until(QUALIFIER, ARGLIST)
|
868
|
+
end # case c
|
869
|
+
|
870
|
+
# If we're processing a template name, then '$' is allowed to
|
871
|
+
# end the name.
|
872
|
+
|
873
|
+
c = input()
|
874
|
+
|
875
|
+
assert(c == nil || c == QUALIFIER || c == ARGLIST)
|
876
|
+
|
877
|
+
if c == QUALIFIER
|
878
|
+
c = advance()
|
879
|
+
|
880
|
+
if @set_qual
|
881
|
+
@prevqual = @qualend && @qualend.dup
|
882
|
+
@qualend = @target.dup
|
883
|
+
end
|
884
|
+
|
885
|
+
append '::'
|
886
|
+
|
887
|
+
if (c == 0)
|
888
|
+
@kind = (@kind & ~UM_KINDMASK) | UM_VTABLE
|
889
|
+
end
|
890
|
+
else
|
891
|
+
break
|
892
|
+
end
|
893
|
+
end # while true
|
894
|
+
end # def copy_name
|
895
|
+
|
896
|
+
# umKind unmangle(src, dest, maxlen, qualP, baseP, doArgs)
|
897
|
+
#
|
898
|
+
# doArgs
|
899
|
+
# if this argument is non-0 (aka TRUE), it means that when
|
900
|
+
# unmangling a function name, its arguments should also be
|
901
|
+
# unmangled as part of the output name. Otherwise, only the name
|
902
|
+
# will be unmangled, and not the arguments.
|
903
|
+
|
904
|
+
def unmangle src, args = {}
|
905
|
+
return src if !src || src.empty? || src[0] != '@'
|
906
|
+
|
907
|
+
# unmangle args? defaulting to true
|
908
|
+
doArgs = args.fetch(:args, true)
|
909
|
+
|
910
|
+
@vtbl_flags = []
|
911
|
+
@kind = 0
|
912
|
+
@source_string = ''
|
913
|
+
@source = StringPtr.new(@source_string)
|
914
|
+
@result = ''
|
915
|
+
@target = StringPtr.new(@result)
|
916
|
+
|
917
|
+
#return UM_ERROR if src.size > 254
|
918
|
+
@hashstart =
|
919
|
+
case src.size
|
920
|
+
when 254 # old hash for bcc version 0x600 and earlier
|
921
|
+
250
|
922
|
+
when 253 # new hash for bcc version 0x610 and later
|
923
|
+
231
|
924
|
+
else
|
925
|
+
MAXBUFFLEN
|
926
|
+
end
|
927
|
+
|
928
|
+
@savechar = 0
|
929
|
+
|
930
|
+
# All mangled names start with '@' character.
|
931
|
+
|
932
|
+
src = src[1..-1] # skip initial '@'
|
933
|
+
|
934
|
+
# check for Microsoft compatible fastcall names, which are of the form:
|
935
|
+
# @funcName@<one or more digits indicating size of all parameters>
|
936
|
+
return src if src =~ /\A@.*@\d+\Z/
|
937
|
+
|
938
|
+
# Pascal names can not contain lowercase letters
|
939
|
+
if src !~ /[a-z]/
|
940
|
+
# convert uppercase Pascal names to lowercase
|
941
|
+
src.downcase!
|
942
|
+
end
|
943
|
+
|
944
|
+
# This is at LEAST a member name, if not a fully mangled template
|
945
|
+
# or function name. So, begin outputting the subnames. We set up
|
946
|
+
# the pointers in globals so that we don't have to pass
|
947
|
+
# everything around all the time.
|
948
|
+
|
949
|
+
@kind = UM_UNKNOWN
|
950
|
+
@source_string = src
|
951
|
+
@source = StringPtr.new(@source_string)
|
952
|
+
@prevqual = @qualend = @base_name = @base_end = nil
|
953
|
+
@set_qual = 1
|
954
|
+
|
955
|
+
# Start outputting the qualifier names and the base name.
|
956
|
+
|
957
|
+
@namebase = @target.dup
|
958
|
+
|
959
|
+
copy_name(0)
|
960
|
+
@set_qual = 0
|
961
|
+
@base_end = @target.dup
|
962
|
+
|
963
|
+
if (@kind & UM_KINDMASK) == UM_TPDSC || (@kind & UM_SPECMASK) != 0
|
964
|
+
p = strchr(@namebase, ' ')
|
965
|
+
assert(p)
|
966
|
+
@namebase = p + 1
|
967
|
+
end
|
968
|
+
|
969
|
+
if (@kind & UM_KINDMASK) == UM_CONSTRUCTOR || (@kind & UM_KINDMASK) == UM_DESTRUCTOR
|
970
|
+
start = nil
|
971
|
+
|
972
|
+
if (@kind & UM_KINDMASK) == UM_DESTRUCTOR
|
973
|
+
copy_char('~')
|
974
|
+
end
|
975
|
+
|
976
|
+
if !@qualend
|
977
|
+
# It's a bcc-created static constructor??
|
978
|
+
# give it a name.
|
979
|
+
copy_string("unknown")
|
980
|
+
else
|
981
|
+
if !@prevqual
|
982
|
+
start = @namebase.dup
|
983
|
+
else
|
984
|
+
start = @prevqual + 2
|
985
|
+
end
|
986
|
+
len = @qualend - start
|
987
|
+
copy_string(start, len)
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
# If there's a function argument list, copy it over in expanded
|
992
|
+
# form.
|
993
|
+
|
994
|
+
if input() == ARGLIST && doArgs # function args
|
995
|
+
c = advance()
|
996
|
+
assert(c == 'q' || c == 'x' || c == 'w')
|
997
|
+
|
998
|
+
# Output the function parameters, and return type in the case
|
999
|
+
# of template function specializations.
|
1000
|
+
|
1001
|
+
@set_qual = 0
|
1002
|
+
@adjust_quals = 1
|
1003
|
+
|
1004
|
+
copy_type(@namebase, 0)
|
1005
|
+
|
1006
|
+
if ((@kind & UM_KINDMASK) == UM_UNKNOWN)
|
1007
|
+
@kind |= UM_FUNCTION
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
elsif ((@kind & UM_KINDMASK) == UM_UNKNOWN)
|
1011
|
+
@kind |= UM_DATA
|
1012
|
+
elsif @vtbl_flags.any?
|
1013
|
+
copy_string(" (" + @vtbl_flags.join(", ") + ")")
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
# Put some finishing touches on the @kind of this entity.
|
1017
|
+
|
1018
|
+
if (@qualend)
|
1019
|
+
@kind |= UM_QUALIFIED
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
# If the user wanted the qualifier and base name saved, then do it now.
|
1023
|
+
|
1024
|
+
# TODO
|
1025
|
+
# if (@kind & UM_ERRMASK) == 0
|
1026
|
+
# if @qualend
|
1027
|
+
# len = @qualend - @namebase
|
1028
|
+
# @qualP = @namebase[0, len]
|
1029
|
+
# end
|
1030
|
+
#
|
1031
|
+
# if @base_name
|
1032
|
+
# len = @base_end - @base_name
|
1033
|
+
# @baseP = @base_name[0, len]
|
1034
|
+
# end
|
1035
|
+
# end
|
1036
|
+
|
1037
|
+
# move '__fastcall' to start of the string if its found in middle of string
|
1038
|
+
pos = @result.index(" __fastcall ")
|
1039
|
+
if pos && pos != 0
|
1040
|
+
@result = "__fastcall " + @result.sub("__fastcall ", "")
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
# sometimes const args are marked "const const",
|
1044
|
+
# original tdump.exe tool also have this bug
|
1045
|
+
@result.gsub! "const const ", "const "
|
1046
|
+
|
1047
|
+
# doArgs implicitly includes calling convention, but '__tpdsc__' is always
|
1048
|
+
# returned by original code, so strip it here if doArgs == false
|
1049
|
+
unless doArgs
|
1050
|
+
@result.sub! /\A__tpdsc__ /,''
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
@result
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
# same as 'unmangle', but catches all exceptions and returns original name
|
1057
|
+
# if can not unmangle
|
1058
|
+
def safe_unmangle name, *args
|
1059
|
+
unmangle name, *args
|
1060
|
+
rescue
|
1061
|
+
name
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
def self.unmangle *args
|
1065
|
+
new.unmangle(*args)
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
def self.safe_unmangle *args
|
1069
|
+
new.safe_unmangle(*args)
|
1070
|
+
end
|
1071
|
+
end # class Unmangler
|
1072
|
+
|
1073
|
+
###################################################################
|
1074
|
+
|
1075
|
+
if $0 == __FILE__
|
1076
|
+
require 'awesome_print'
|
1077
|
+
require 'pp'
|
1078
|
+
|
1079
|
+
def check src, want, do_args = true
|
1080
|
+
u = Unmangler::Borland.new
|
1081
|
+
got = nil
|
1082
|
+
begin
|
1083
|
+
got = u.unmangle(src, do_args)
|
1084
|
+
rescue
|
1085
|
+
pp u
|
1086
|
+
raise
|
1087
|
+
end
|
1088
|
+
if got == want
|
1089
|
+
print ".".green
|
1090
|
+
else
|
1091
|
+
puts "[!] want: #{want.inspect}"
|
1092
|
+
puts "[!] got: #{got.inspect}"
|
1093
|
+
pp u
|
1094
|
+
puts
|
1095
|
+
exit 1
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
check "@afunc$qxzcupi", "afunc(const signed char, int *)"
|
1100
|
+
check "@foo$qpqfi$d", "foo(double (*)(float, int))"
|
1101
|
+
check "@myclass@func$qil","myclass::func(int, long)"
|
1102
|
+
|
1103
|
+
check "@Sysinit@@InitExe$qqrpv",
|
1104
|
+
"__fastcall Sysinit::__linkproc__ InitExe(void *)"
|
1105
|
+
|
1106
|
+
check "@Forms@TApplication@SetTitle$qqrx17System@AnsiString",
|
1107
|
+
"__fastcall Forms::TApplication::SetTitle(const System::AnsiString)"
|
1108
|
+
|
1109
|
+
check "@Forms@TApplication@CreateForm$qqrp17System@TMetaClasspv",
|
1110
|
+
"__fastcall Forms::TApplication::CreateForm(System::TMetaClass *, void *)"
|
1111
|
+
|
1112
|
+
check "@System@@LStrCatN$qqrv", "__fastcall System::__linkproc__ LStrCatN()"
|
1113
|
+
|
1114
|
+
check "@System@DynArraySetLength$qqrrpvpvipi",
|
1115
|
+
"__fastcall System::DynArraySetLength(void *&, void *, int, int *)"
|
1116
|
+
|
1117
|
+
check "@System@Variant@PutElement$qqrrx14System@Variantxixi",
|
1118
|
+
"__fastcall System::Variant::PutElement(System::Variant&, const int, const int)"
|
1119
|
+
|
1120
|
+
check "@Windows@HwndMSWheel$qqrruit1t1rit4",
|
1121
|
+
"__fastcall Windows::HwndMSWheel(unsigned int&, unsigned int&, unsigned int&, int&, int&)"
|
1122
|
+
|
1123
|
+
# IDA uses '__int64' instead of 'long long'
|
1124
|
+
check "@Sysutils@TryStrToInt64$qqrx17System@AnsiStringrj",
|
1125
|
+
"__fastcall Sysutils::TryStrToInt64(const System::AnsiString, long long&)"
|
1126
|
+
|
1127
|
+
check "@Sysutils@Supports$qqrpx14System@TObjectrx5_GUIDpv",
|
1128
|
+
"__fastcall Sysutils::Supports(System::TObject *, _GUID&, void *)"
|
1129
|
+
|
1130
|
+
check "@std@%vector$51boost@archive@detail@basic_iarchive_impl@cobject_id69std@%allocator$51boost@archive@detail@basic_iarchive_impl@cobject_id%%@$bsubs$qui",
|
1131
|
+
"std::vector<boost::archive::detail::basic_iarchive_impl::cobject_id, std::allocator<boost::archive::detail::basic_iarchive_impl::cobject_id> >::operator [](unsigned int)"
|
1132
|
+
|
1133
|
+
check "@Dbcommon@GetTableNameFromSQLEx$qqrx17System@WideString25Dbcommon@IDENTIFIEROption",
|
1134
|
+
"__fastcall Dbcommon::GetTableNameFromSQLEx(const System::WideString, Dbcommon::IDENTIFIEROption)"
|
1135
|
+
|
1136
|
+
# check "@Sysutils@Supports$qqrx45System@_DelphiInterface$t17System@IInterface_rx5_GUIDpv",
|
1137
|
+
# "__fastcall Sysutils::Supports(const System::DelphiInterface<System::IInterface>, _GUID const &, void *)"
|
1138
|
+
|
1139
|
+
####################################
|
1140
|
+
# w/o args
|
1141
|
+
####################################
|
1142
|
+
|
1143
|
+
check "@Dbcommon@GetTableNameFromSQLEx$qqrx17System@WideString25Dbcommon@IDENTIFIEROption",
|
1144
|
+
"Dbcommon::GetTableNameFromSQLEx",
|
1145
|
+
false
|
1146
|
+
|
1147
|
+
check "@std@%vector$51boost@archive@detail@basic_iarchive_impl@cobject_id69std@%allocator$51boost@archive@detail@basic_iarchive_impl@cobject_id%%@$bsubs$qui",
|
1148
|
+
"std::vector<boost::archive::detail::basic_iarchive_impl::cobject_id, std::allocator<boost::archive::detail::basic_iarchive_impl::cobject_id> >::operator []",
|
1149
|
+
false
|
1150
|
+
|
1151
|
+
puts
|
1152
|
+
end
|